mirror of
https://github.com/documenso/documenso.git
synced 2025-11-23 05:01:54 +10:00
Merge branch 'main' into feat/automated-fields-signature
This commit is contained in:
@ -0,0 +1,82 @@
|
||||
---
|
||||
title: Cal.com Chooses Documenso for DPA and BAA Scalability and Compliance
|
||||
description: Learn how Cal.com scales their Data Processing Agreement (DPA) and Business Associate Agreement (BAA) processes with Documenso’s open source platform as they grow.
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2024-10-11
|
||||
tags:
|
||||
- Customer Story
|
||||
- Open Startup
|
||||
- Open Source
|
||||
---
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/cal2.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Scheduling Infrastructure for Everyone"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
Scheduling Infrastructure for Everyone.
|
||||
</figcaption>
|
||||
</figure>
|
||||
TL;DR: Cal.com uses Documenso’s template direct links to facilitate low-friction compliance paperwork, enhancing scalability and user experience.
|
||||
|
||||
## Cal.com – The Most Public Private Company
|
||||
|
||||
[Cal.com](Cal.com) is an open source company that needs no introduction. Founded in 2021 by Bailey Pumfleet and Peer Richelsen, it quickly evolved from an open source alternative to the widespread but limited scheduling platform Calendly into the internet’s most beloved scheduling solution. Starting with just two founders, Cal.com has grown into a team of 22, facilitating millions of bookings per year for its ever-growing user and customer base.
|
||||
|
||||
Their commitment to transparency is evident as they follow the [open startup movement](https://cal.com/open), opening up not only their source code but also providing insights into their business operations. Their Commercial Open Source Software (COSS) model, combining a company and an open source project, has inspired a whole cohort of startups joining the space—not least of which is Documenso.
|
||||
|
||||
## The Need
|
||||
|
||||
At this point, Cal.com serves customers of all sizes, from single users to large enterprises. To provide the best product for their customers, they are certified for SOC 2, HIPAA, GDPR, and many other compliance regulations. One challenge that comes with this is the increasing number of waivers that need to be signed when onboarding customers. Business Associate Agreements (BAAs) and Data Processing Agreements (DPAs) are two of the more commonly known examples. To get these signed with minimal effort for both sides, they were looking for a solution to handle these at scale.
|
||||
|
||||
> We love open source.
|
||||
|
||||
— Peer Richelsen, Co-Founder, Cal.com
|
||||
|
||||
Being an open source company, they also prefer open source in their vendors—for both the shared philosophy and the higher level of trust. The goal was to integrate signing into the checkout process as seamlessly as possible.
|
||||
|
||||
## The Solution
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/cal.png"
|
||||
width="1260"
|
||||
height="630"
|
||||
alt="Cal.com direct link template to sign a DPA"
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
Sign a DPA with Cal by clicking a link anytime.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
Documenso offers exactly this solution through direct link templates, enabling Cal.com to:
|
||||
|
||||
- Provide Immediate Access: Customers can access and sign necessary compliance documents through direct links at any time.
|
||||
- Enhance User Experience: Users are immediately forwarded to onboarding after signing.
|
||||
- Ensure Easy Access: The documents are stored within the company’s team account, allowing easy access for anyone who needs them.
|
||||
|
||||
Direct Link templates can also easily be embedded, using the [Documenso widget](https://documen.so/embedded). Embedding anywhere, pre-Filling the templates and notfiying the compliance team at certain point of the flow are a few of the many option the team now has in continously enhanceing their onboard and compliance UX.
|
||||
|
||||
Read more about our direct link templates here: [Direct Link Signing](https://docs.documenso.com/users/direct-links).
|
||||
|
||||
## The Journey
|
||||
|
||||
Initially, Cal.com’s team approached the new solution with skepticism. As Bailey reflected:
|
||||
|
||||
> We were intrigued but skeptical at first, as we put a lot of thought into compliance and doing things right. Documenso’s documentation and support showcased how their direct link templates could meet our needs while being highly compliant.
|
||||
|
||||
This experience highlights Documenso’s trust philosophy. We strive to be transparent in everything we do and let people judge for themselves. It also shows that we neither want nor get trust by default. Doing things right is a conversation worth having, especially in a space as opaque as digital signatures. It goes without saying that the whole team is hyped to have Cal.com on board and yet another open source company joining the open signing movement 🚀
|
||||
|
||||
If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord).
|
||||
|
||||
Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales)
|
||||
|
||||
Best from Hamburg\
|
||||
Timur
|
||||
@ -0,0 +1,85 @@
|
||||
---
|
||||
title: 'Customer Story Prisma: 4 Reasons why Prisma chose Documenso for Signatures'
|
||||
description: We are happy to welcome Prisma, another OSS company, as a customer. Read here why they choose us.
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2024-09-26
|
||||
tags:
|
||||
- Prisma
|
||||
- Customer Story
|
||||
- Open Source
|
||||
---
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/prisma.png"
|
||||
width="1200"
|
||||
height="675"
|
||||
alt="Primsa Landing Page We simplify database migration, connection pooling, database queries, and readable data models."
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
Prisma uses Documenso for collaborative team signing.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
> TLDR; Prisma is now using Documenso, and [we added visibility scopes](https://docs.documenso.com/users/document-visibility)
|
||||
|
||||
# Prisma
|
||||
|
||||
Prisma is an open-source company known for its modern OSS ORM (Object-Relational Mapping) tools that simplify database interactions for developers. Their flagship product, Prisma ORM, provides a type-safe way to query databases like PostgreSQL, MySQL, and many more. With the addition of Prisma Studio, an intuitive database management interface, Prisma makes it easier and more efficient for developers to work with databases. With their new additions, Prisma Pulse and Accelerate, you can react to real-time database changes and optimize your queries. And they are completely [open source](https://github.com/prisma/prisma)!
|
||||
|
||||
# We choose Prisma too!
|
||||
|
||||
I discovered Prisma when planning the tech stack for the [first version of Documenso](https://github.com/documenso/documenso/releases/tag/0.9-developer-preview). Prisma has felt natural to use since day one and has been the base of our database architecture ever since. It's great to see them develop and grow with us.
|
||||
|
||||
# Why they choose us
|
||||
|
||||
## 1. Signature Flows
|
||||
|
||||
Documenso signing flows are highly configurable, designed to adapt to the needs of any document signing process. Whether you're working with different roles, varying settings, or specific delivery methods, Documenso offers the flexibility to suit your requirements. You can choose to send documents via email, share a manual link, generate a link through the API, or even use a static direct link for quick access—all while ensuring a smooth signing experience.
|
||||
|
||||
Additionally, you can create templates to streamline and reuse common workflows, saving valuable time. Direct link templates enable users to drive the flow themselves, providing a straightforward path for signing. For a seamless experience, Documenso also allows you to embed the signing process directly into your website, ensuring an uninterrupted, integrated workflow tailored to your needs.
|
||||
|
||||
## 2. Modern UX
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/dsux.png"
|
||||
width="1200"
|
||||
height="675"
|
||||
alt="A completed document in Documenso, ready to download."
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
We call Documenso's design "Happy Minimalism"
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
We’ve crafted Documenso with a sleek, modern interface that makes it incredibly easy to use. Whether you’re signing documents, managing workflows, or fine-tuning settings, its intuitive design allows you to accomplish tasks quickly and effortlessly. More than just powerful, Documenso is a pleasure to navigate—designed to be accessible to everyone, no matter their level of tech experience.
|
||||
|
||||
## 3. Teams
|
||||
|
||||
### Teamwork Makes the Dream Work
|
||||
|
||||
Documenso makes teamwork a breeze with its team management features. You can easily set up and organize teams, making it simple to share and manage documents and workflows together. This is a lifesaver for larger organizations or teams spread across different departments, ensuring everyone stays in sync and on track. Different visibility scopes ensure private documents stay private and others are shared for easy collaboration.
|
||||
|
||||
### Document Visibility
|
||||
|
||||
Collaboration within a team often demands different levels of access to documents. For instance, the Documenso team at Prisma needed a way to set custom visibility on some documents while keeping others accessible to everyone. To address this need, we introduced role-based visibility scopes. This feature allows teams to manage documents more effectively. They can make certain documents visible only to managers or, in special cases, restricted to admins. This ensures sensitive information stays protected while general documents remain accessible to those who need them.
|
||||
|
||||
Learn more about visibility scopes and [how they can benefit your team here](https://docs.documenso.com/users/document-visibility).
|
||||
|
||||
## 4. OSS!
|
||||
|
||||
As you might know, we are open-source! This means you can peek under the hood, tweak things to your liking, and even contribute to making the platform better. We love the community-driven aspect of open-source, and it aligns perfectly with our goal to keep improving and innovating with input from our users.
|
||||
|
||||
So, whether you're looking to streamline your document workflows or just need a solid, reliable platform, Documenso has got your back. And we're thrilled to serve another OSS company and help make the space more open.
|
||||
|
||||
If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord).
|
||||
|
||||
Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales)
|
||||
|
||||
Best from Hamburg\
|
||||
Timur
|
||||
86
apps/marketing/content/blog/go-fork-yourself.mdx
Normal file
86
apps/marketing/content/blog/go-fork-yourself.mdx
Normal file
@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Go Fork Yourself
|
||||
description: Curious about our take on open-source and code forking? Discover why we see forking not as a threat but as a vital part of the Open Source ecosystem.
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2024-10-03
|
||||
tags:
|
||||
- Culture
|
||||
- Open Startup
|
||||
- Open Source
|
||||
---
|
||||
|
||||
> TLDR; At Documenso, we see OSS as co-owned by all. Forking—collaborative or not—is part of the open-source spirit.
|
||||
|
||||
## Freedom vs. Ownership
|
||||
|
||||
Recently, there has been a lot of debate on the subject of forks and the usage of OSS IP (Open Source Software Intellectual Property). While I mostly aim to stay out of these controversies (as there is no “winning”), I wanted to take this opportunity to share my views on IP and forking culture here at Documenso. I don’t presume this is the ideal path, but for me, it’s the only path that makes sense.
|
||||
|
||||
What these issues show foremost, in my opinion, is that the concept of Open Source is still evolving. I have heard many say, “Open Source is clearly defined” and that there is no ambiguity anymore. That may be true on the legal side, but there are vast differences in how these rules are interpreted and lived out. Here are a few questions to illustrate the point:
|
||||
|
||||
1. Is it okay to use an open-source project without ever giving back?
|
||||
2. Is it okay to fork (some might say copy) an OSS product and build something on top of it?
|
||||
3. Are we morally obliged to fight those who provide different answers to these questions than we do?
|
||||
|
||||
## Embracing Forks and Collaboration
|
||||
|
||||
Since starting Documenso, I’ve thought a lot about what it actually means to be Open Source for us. So far, it has been about openness in working with everyone, from contributors to customers and sharing our work transparently. For this, we have been richly rewarded with attention and reach. This collaborative give-and-take is what people commonly associate with being Open Source, and it seems ideal.
|
||||
|
||||
Yet, there are the questions mentioned above. And while these may be contentious, my take is straightforward:
|
||||
|
||||
1. Yes.
|
||||
2. Yes.
|
||||
3. No.
|
||||
|
||||
I say this because, to me, the principles of Open Source are rooted in freedom and collaboration. That means allowing others to use, improve, or even compete with what you’ve built without feeling possessive over the code. The beauty of Open Source lies in its openness—its ability to be forked, reused, and adapted by anyone.
|
||||
|
||||
You may answer these questions differently for your own reasons. One thing I’ve found lacking in the discourse is the fact that Open Source is still being treated as socially proprietary. If it’s under an open-source license, you can fork it and try to improve upon the original, and there’s nothing wrong with that. The same is true for closed-source startups. Yet in Open Source, there’s a notion that it’s somehow “dirty,” even though the license explicitly allows it.
|
||||
|
||||
## Forking in Action: Real-World Examples
|
||||
|
||||
When the team behind **Node.js** disagreed with its governance and pace of development, they forked the project to create **io.js**. This wasn’t seen as dirty but as a necessary push for change. In fact, the fork resulted in positive changes—better community governance and faster development—which eventually led to the merge of the two projects under the Node.js Foundation. It shows that forking can be a catalyst for improvement, not just competition.
|
||||
|
||||
## The Misconception of “Exploitative” Usage
|
||||
|
||||
However, sometimes forks don’t merge back but still bring positive change. A good example is **Jenkins**, which was forked from **Hudson** over disagreements in governance after Oracle acquired Sun Microsystems. Jenkins quickly overtook Hudson in terms of community support, development, and innovation. Rather than being seen as a hostile move, the fork enabled Jenkins to become a thriving project, better aligned with the open-source ethos of collaboration and transparency. It emphasizes that forking isn’t inherently exploitative; it can simply be a way to realize a project’s full potential.
|
||||
|
||||
And then there’s **MariaDB**, a fork of **MySQL**. After Oracle acquired MySQL, many in the community feared the project’s open-source nature could be compromised. The fork preserved its spirit, and MariaDB has since grown to become a popular and thriving database. It’s a reminder that sometimes, forking is not just acceptable—it’s necessary to uphold the values and freedoms of open-source software.
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/owncode.jpeg"
|
||||
width="1200"
|
||||
height="675"
|
||||
alt="Meme: If everyone owns the code, no one does."
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">
|
||||
Funny Meme to drive the point home.
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
My view is that the code is not “your” code, just as Documenso’s code is not “our” code. It’s been co-owned by the world ever since we published the repo under AGPL V3. That is the whole point. It’s finally not owned by anyone (cue the “everyone/no one” meme). Open Source is for everyone, even competitors. Yet, we are still treating the licenses as extensions of the old, proprietary world and defending perceived injustices based on that model.
|
||||
|
||||
> Side Note: Full compliance with all license and other legal rules is a given here.
|
||||
|
||||
## Documenso’s Approach: Co-Ownership and Community
|
||||
|
||||
So, if you want to fork Documenso and build a business on it, you can. Whether that’s a cool thing to do is another matter. Whether you do a better job than us is also another matter (you won’t). But if you do, I’ll be the first to join. But why not join us from the start since you already have the upside? We exist because we believe this to be the best way forward—not because we force it.
|
||||
|
||||
## The Bigger Picture: Open-Source as Progress
|
||||
|
||||
I’ve also thought a lot about question #3. I understand the impulse to fight anyone who doesn’t appreciate this collaborative approach, but there is no part of this model that backs that up. You are free to “exploit” as long as it’s in a way that adds value. The fallacy is in considering someone else using the OSS part for their business as treason, which it’s not. It’s the whole point.
|
||||
|
||||
While some might say this is theoretical and that reality is different, this is the version of Open Source on which we are building Documenso. The point here is that OSS companies must be resilient to handle forking and competition; without this resilience, an open source driven economy can’t thrive. The focus on freedom and collaboration means being prepared for forks and challenges as part of the growth, not as threats.
|
||||
|
||||
Of course, all of this applies to Documenso, the OSS project, not Documenso Inc., the company, which is very much a privately owned, for-profit entity. However, since the goal is to scale Documenso to the entire world, there is plenty of room to see everyone as co-owners of the Open Source project rather than as competitors. In the end, Open Source is about progress through freedom. If you don’t like how we run things, go fork yourself and hold us accountable. We don’t own this; we just happened to start it.
|
||||
|
||||
> Since this article is open source as well, you are free to fork it and change it here: [https://documen.so/repo](https://documen.so/repo)
|
||||
|
||||
If you have any questions or comments, please reach out on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord).
|
||||
|
||||
Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales)
|
||||
|
||||
Best from Hamburg\
|
||||
Timur
|
||||
72
apps/marketing/content/blog/introducing-embedding.mdx
Normal file
72
apps/marketing/content/blog/introducing-embedding.mdx
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
title: 'Introducing Embedding Support for Documenso'
|
||||
description: 'Embedding is now here! Learn how we built it and how it can be used to bring e-signing to your own applications.'
|
||||
authorName: 'Lucas Smith'
|
||||
authorImage: '/blog/blog-author-lucas.png'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2024-09-06
|
||||
tags:
|
||||
- Development
|
||||
---
|
||||
|
||||
When we first launched Documenso, one of the most requested features was embedding. We knew it was important and aligned with our desire to not just be a e-signing application but to instead provide the e-signature infrastructure for the web and beyond.
|
||||
|
||||
With that said, we decided to hold off initially so we could focus on building a solid, well-featured core application. Looking back, this was definitely the right call. Embedding is only as good as the features behind it, and we didn't want to release something that wasn't ready to meet user and developer expectations.
|
||||
|
||||
Over the past year, we've been busy adding tons of new features and reaching new levels of compliance, like 21 CFR Part 11. We've also introduced [new fields](/blog/introducing-advanced-signing-fields), [built out an API](/blog/public-api), [added webhooks, integrations with Zapier](/blog/launch-week-2-day-4), and a lot more.
|
||||
|
||||
Now that we've laid a solid foundation, it's finally time to focus on embedding, the top-requested feature from both our users and those self-hosting our platform.
|
||||
|
||||
## Why Embedding Took Time
|
||||
|
||||
In previous projects, I’ve often seen embedding built by bundling components for use in a client’s website or app. This method gives users maximum flexibility for styling and behavior, while avoiding certain cross-origin issues. However, it can also introduce problems like code conflicts or performance bottlenecks. For example, third-party tools such as Google Tag Manager (GTM) or other marketing scripts can interfere with your SDK. Additionally, the SDK must remain lightweight to avoid slowing down the client’s page.
|
||||
|
||||
For Documenso, we decided to explore a different approach. After carefully researching our options, we opted for an iframe-based solution. While iframes are typically less flexible—especially when it comes to theming or passing pre-filled data containing personally identifiable information (PII)—we identified ways to mitigate these concerns.
|
||||
|
||||
One of the biggest challenges was ensuring that we could pass sensitive data, like emails for pre-filling forms, without exposing PII to our server. To solve this, we used [fragment identifiers](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) in the URL, which are processed client-side and never sent in network requests. This method ensures that PII is protected and not logged by our server or any intermediate web services.
|
||||
|
||||
### Using the PostMessage API for Communication
|
||||
|
||||
To maintain a high level of interactivity, our iframes communicate with the parent window using the [postMessage API](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage). This allows us to notify the parent app when specific events occur inside the iframe, creating a more dynamic user experience and bridging the gap between our iframe-based solution and typical fat SDKs.
|
||||
|
||||
Additionally, props are passed into the iframe via the [fragment identifier](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) of the URL. This avoids the need for complex two-way data synchronization between the parent and child frames, making the system stable and more reliable.
|
||||
|
||||
### Building the Embeds with Mitosis
|
||||
|
||||
Given that our iframe solution is quite lightweight, we saw this as a great opportunity to experiment with [Mitosis](https://mitosis.builder.io/) which would let us do something truly special. For those unfamiliar, Mitosis is a project by Builder.io that lets you write components once and then transpile them into a variety of frameworks like React, Vue, and Svelte.
|
||||
|
||||
We used Mitosis to build two key components: a direct template embed and a document signing embed. The direct template allows users to use a template as if it were an evergreen document—meaning that, when someone completes the template, a new document is automatically generated. This is the use case we expect most users to adopt for embedding. For more advanced workflows, we also offer a document signing embed, which can handle multi-recipient workflows and other complex scenarios intended for use in deeper, rich integrations.
|
||||
|
||||
Mitosis allowed us to quickly target several popular frameworks, including [React](https://www.npmjs.com/package/@documenso/embed-react), [Preact](https://www.npmjs.com/package/@documenso/embed-preact), [Vue](https://www.npmjs.com/package/@documenso/embed-vue), [Svelte](https://www.npmjs.com/package/@documenso/embed-svelte), and [SolidJS](https://www.npmjs.com/package/@documenso/embed-solid).
|
||||
|
||||
I had also hoped to include Angular, but while Mitosis makes it really easy to transpile component, we still have to take care of bundling and packaging the resulting component ourselves. While the above frameworks can all be bundled using Vite.js, Angular still has it's own set of tooling that we would need to learn and use. Given this constraint we opted to put Angular on hold for now while we wait for the newer Vite.js support to mature.
|
||||
|
||||
### Challenges and Lessons with Mitosis and more
|
||||
|
||||
While our experience with Mitosis was largely positive, there were some challenges along the way. For instance, certain state properties with the same names as props caused issues during the transpilation process, leading to type errors and unexpected transpilation results with some targets.
|
||||
|
||||
This was also a challenge since our initial implementation of the two components had some minor separation of concerns which also resulted in some transpilation issues with some targets. We addressed this by removing the separation of concerns for now since it was mostly for show rather than out of necessity.
|
||||
|
||||
On top of that, packaging and publishing the embeds posed its own set of challenges, particularly given the growing complexity of JavaScript package management. Tools like [Publint](https://www.npmjs.com/package/publint) helped streamline the process by ensuring we followed best practices for both CommonJS and ESM formats.
|
||||
|
||||
### To the Future, The Documenso Platform
|
||||
|
||||
With the embedding feature now in place, we're excited to continue expanding Documenso's capabilities. Embeds are just the beginning of what we're calling the Documenso platform. Through our user research, we've learned that while many businesses appreciate having a flexible e-signature solution, they're even more interested in using our tools to build signing functionality directly into their own apps—without worrying about the technical complexities of compliance and security that come with e-signing.
|
||||
|
||||
Over the coming months, we'll be working on enhancing our API, strengthening integrations with tools like Zapier, and improving our webhook system. Our goal is to give users the ability to embed e-signatures and document management wherever they need it, whether that's through self-hosting or by using Documenso as a platform. We can't wait to see how our users and self-hosters leverage these new capabilities!
|
||||
|
||||
### Ready to Get Started?
|
||||
|
||||
If you're ready to embed document signing into your own app or website, check out our [Embedding Documentation](https://docs.documenso.com/developers/embedding?utm_source=blog&utm_campaign=introducing-embedding) to see how easy it is to get started. You'll find everything you need to get started today!
|
||||
|
||||
<video
|
||||
src="/blog/introducing-embedding/embedding-demo.mp4"
|
||||
className="aspect-video w-full"
|
||||
autoPlay
|
||||
loop
|
||||
controls
|
||||
/>
|
||||
|
||||
We're always here to help! If you have questions or need support, join our [Discord](https://documen.so/discord) or [book a demo](https://documen.so/book-a-demo). We'd love to hear how you're using Documenso or wanting to use Documenso to enhance your workflow.
|
||||
|
||||
Stay tuned for more updates as we continue to evolve the Documenso platform and make it even easier to bring document signing into your workflows.
|
||||
64
apps/marketing/content/blog/project-babel.mdx
Normal file
64
apps/marketing/content/blog/project-babel.mdx
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
title: Project Babel
|
||||
description: We are announcing Project Babel - an initiative to support all languages of the world on Documenso.
|
||||
authorName: 'Timur Ercan'
|
||||
authorImage: '/blog/blog-author-timur.jpeg'
|
||||
authorRole: 'Co-Founder'
|
||||
date: 2024-11-08
|
||||
tags:
|
||||
- Languages
|
||||
- Community
|
||||
- Open Source
|
||||
---
|
||||
|
||||
<figure>
|
||||
<MdxNextImage
|
||||
src="/blog/babel.png"
|
||||
width="800"
|
||||
height="800"
|
||||
alt=""
|
||||
/>
|
||||
|
||||
<figcaption className="text-center">The tower of Babel to add some Gravitas to this project.</figcaption>
|
||||
</figure>
|
||||
|
||||
> TLDR; We are opening up translations to the community. Read this to add a language: https://documen.so/babel-fish
|
||||
|
||||
## Announcing Project Babel: Powering Documenso with Global Language Support
|
||||
|
||||
At Documenso, we believe that open source is more than just a software philosophy—it’s a way to build solutions that are open to all. Now, we’re happy to take that mission further with Project Babel, a community-driven initiative designed to bring worldwide language support to the Documenso platform. This project aims to enable Documenso to support as many languages as possible.
|
||||
|
||||
## Why Language Support Matters
|
||||
|
||||
We already have customers from 36 different countries and are seeing traffic from even more. When it comes to critical tools like signature platforms, having a user interface in your native language can make all the difference. No matter who and where you are, your team deserves tools that are fully accessible and intuitive. That’s why we’re making it our goal to support every language, and we need your help to make it happen! We’re building Documenso as a truly global public commodity.
|
||||
|
||||
## The Vision Behind Project Babel
|
||||
|
||||
The goal of Project Babel is simple but bold: We want to out-ship and out-customize every other document signing tool worldwide. How? By leveraging the collective power of our global community.
|
||||
|
||||
Unlike closed-source software, where localization means waiting for updates from the core team, Project Babel lets anyone contribute a new language, improve an existing translation, or customize the experience to meet local cultural nuances. This flexibility isn’t just a bonus—it’s the baseline for truly global products.
|
||||
|
||||
Through Project Babel, you can help make Documenso the most inclusive e-signature tool. Whether by adding a language you speak or fine-tuning existing translations, you’re shaping a platform that works for everyone, everywhere.
|
||||
|
||||
## How You Can Contribute
|
||||
|
||||
We’ve created a simple GitHub-based contribution flow to get started. We’ll improve the flow and user experience as the project progresses. As always, your contributions are highly valued.
|
||||
|
||||
Check out the contribution guide here: [https://documen.so/babel-fish](https://documen.so/babel-fish)
|
||||
|
||||
## Open Source Makes It Possible
|
||||
|
||||
Closed-source solutions can’t keep up with the speed or depth of customization that open source offers. While other companies might take months or years to localize their products, Documenso can adapt and grow in real-time, thanks to contributions from our community. Whether it’s a small regional dialect or a widely spoken language, Project Babel ensures that Documenso evolves to meet the needs of people everywhere.
|
||||
|
||||
> More importantly, this initiative empowers users. It allows you to control your software experience, ensuring it reflects your culture, language, and unique needs.
|
||||
|
||||
Project Babel is more than a localization effort—it’s the first step toward democratizing access to highly customized software for everyone, no matter where they are or what language they speak. We’re incredibly excited about this initiative, but it can only succeed with your participation. We invite you to join us in making Documenso the most linguistically inclusive platform out there.
|
||||
|
||||
Ready to get started? Check out the full tutorial and become part of the Babel community today! Let’s build open signing for the world: https://documen.so/babel-fish
|
||||
|
||||
If you have any questions or comments, reach out on [Twitter / X](https://twitter.com/eltimuro) (DMs open) or [Discord](https://documen.so/discord).
|
||||
|
||||
Thinking about switching to a modern signing platform? Reach out anytime: [https://documen.so/sales](https://documen.so/sales)
|
||||
|
||||
Best from Hamburg\
|
||||
Timur
|
||||
@ -8,6 +8,135 @@ Check out what's new in the latest version and read our thoughts on it. For more
|
||||
|
||||
---
|
||||
|
||||
# Documenso v1.7.1: Signing order and document visibility
|
||||
|
||||
We're excited to introduce Documenso v1.7.1, bringing you improved control over your document signing process. Here are the key updates:
|
||||
|
||||
## 🌟 Key New Features
|
||||
|
||||
### 1. Signing Order
|
||||
|
||||
Specify the sequence in which recipients sign your documents. This ensures a structured signing process, particularly useful for complex agreements or hierarchical approvals.
|
||||
|
||||
<video
|
||||
src="/changelog/signing-order-demo.mp4"
|
||||
className="aspect-video w-full"
|
||||
autoPlay
|
||||
loop
|
||||
controls
|
||||
/>
|
||||
|
||||
### 2. Document Visibility
|
||||
|
||||
Manage who can view your documents and when. This feature offers greater privacy and flexibility in your document sharing workflows.
|
||||
|
||||
<video
|
||||
src="/changelog/document-visibility-demo.mp4"
|
||||
className="aspect-video w-full"
|
||||
autoPlay
|
||||
loop
|
||||
controls
|
||||
/>
|
||||
|
||||
## 🔧 Other Improvements
|
||||
|
||||
- Added language switcher for easier language selection
|
||||
- Support for smaller field bounds in documents
|
||||
- Improved select field UX
|
||||
- Enhanced template functionality for advanced fields
|
||||
- Added authOptions to the API
|
||||
- Various UI refinements and bug fixes
|
||||
|
||||
## 💡 Recent Features
|
||||
|
||||
Don't forget about these powerful features from our recent v1.7.0 release:
|
||||
|
||||
- Embedded Signing Experience
|
||||
- Copy and Paste Fields
|
||||
- Customizable Signature Colors
|
||||
|
||||
## 👏 Thank You
|
||||
|
||||
As always, we're grateful for our community's contributions and feedback. Your input continues to shape Documenso into the leading open-source document signing solution.
|
||||
|
||||
We're eager to see how these new features enhance your document workflows. Enjoy exploring Documenso v1.7.1!
|
||||
|
||||
---
|
||||
|
||||
# Documenso v1.7.0: Embedded Signing, Copy and Paste, and More
|
||||
|
||||
We're thrilled to announce the release of Documenso v1.7.0, packed with exciting new features and improvements that enhance document signing flexibility, user experience, and global accessibility.
|
||||
|
||||
We're excited to see what you'll create with this release and we'd love to hear your feedback. Let's dive into the highlights:
|
||||
|
||||
## 🌟 Key Features
|
||||
|
||||
### Embedded Signing Experience
|
||||
|
||||
Take your document signing to the next level with our new embedded signing feature. Now you can seamlessly integrate Documenso's signing process directly into your own website or application, providing a smooth, branded experience for your users.
|
||||
|
||||
<video
|
||||
src="/blog/introducing-embedding/embedding-demo.mp4"
|
||||
className="aspect-video w-full"
|
||||
controls
|
||||
/>
|
||||
|
||||
Check out our [Embedding documentation](https://docs.documenso.com/developers/embedding) to learn more about how to get started.
|
||||
|
||||
### Copy and Paste Fields
|
||||
|
||||
Streamline your document preparation with our new copy and paste functionality for fields. This feature allows you to quickly duplicate fields across your document, saving time and ensuring consistency in your templates.
|
||||
|
||||
### Customizable Signature Colors
|
||||
|
||||
Recipients can now select a signature color from our list of available colors, supporting workflows where specific colors are required for each recipient, location, or document.
|
||||
|
||||
### Enhanced Internationalization (i18n)
|
||||
|
||||
Following on from our last release we've now expanded our i18n support to the main web application. We haven't yet added support for any additional languages but that will be coming quickly now that we have completed the hard work of wrapping all of our content in our new i18n system.
|
||||
|
||||
These enhancements make Documenso more accessible to users worldwide.
|
||||
|
||||
## 🔧 Other Improvements
|
||||
|
||||
- **API Enhancements**:
|
||||
|
||||
- New endpoint to prefill fields via API
|
||||
- Updated createFields API endpoint for more flexibility
|
||||
- Automatically set public profile URL for OIDC users
|
||||
|
||||
- **Security and Performance**:
|
||||
|
||||
- Document sealing moved to a background job for improved performance
|
||||
- Disable 2FA with backup codes for enhanced account recovery options
|
||||
- Extended lifespan for invites and confirmations
|
||||
|
||||
- **User Experience**:
|
||||
|
||||
- Updated email templates to reflect team-specific information
|
||||
- Fixed issues with dialog closing on page refresh
|
||||
- Improved field editing in document templates
|
||||
|
||||
- **Other Items**:
|
||||
- Added Elestio as a one-click deploy option
|
||||
- Updated README for manual self-hosting
|
||||
- New environment variable for internal webapp URL configuration
|
||||
|
||||
## 📚 New Content
|
||||
|
||||
- [Advanced fields article to help you make the most of Documenso's capabilities](/blog/introducing-advanced-signing-fields)
|
||||
- [Embedding blog post to guide you through how we implemented embedding](/blog/introducing-embedding)
|
||||
|
||||
## 👏 Community Contributions
|
||||
|
||||
A big thank you to our vibrant community! This release includes contributions from several new contributors, further enriching Documenso's capabilities.
|
||||
|
||||
We're excited to see how you'll use these new features to streamline your document workflows. As always, we appreciate your feedback and support in making Documenso the best open-source document signing solution available.
|
||||
|
||||
Enjoy exploring v1.7.0!
|
||||
|
||||
---
|
||||
|
||||
# Documenso v1.6.1: Internationalization, Enhanced OIDC, and More
|
||||
|
||||
We're excited to announce the release of Documenso v1.6.1, which brings several improvements to enhance your document signing experience. Here are the key updates:
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.7.0-rc.4",
|
||||
"version": "1.8.0-rc.3",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3001",
|
||||
"build": "next build",
|
||||
"build": "npm run translate:extract --prefix ../../ && turbo run translate:compile && next build",
|
||||
"start": "next start -p 3001",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "next lint --fix",
|
||||
@ -34,7 +34,7 @@
|
||||
"micro": "^10.0.1",
|
||||
"next": "14.2.6",
|
||||
"next-auth": "4.24.5",
|
||||
"next-axiom": "^1.1.1",
|
||||
"next-axiom": "^1.5.1",
|
||||
"next-contentlayer": "^0.3.4",
|
||||
"next-plausible": "^3.10.1",
|
||||
"perfect-freehand": "^1.2.0",
|
||||
|
||||
1
apps/marketing/process-env.d.ts
vendored
1
apps/marketing/process-env.d.ts
vendored
@ -2,6 +2,7 @@ declare namespace NodeJS {
|
||||
export interface ProcessEnv {
|
||||
NEXT_PUBLIC_WEBAPP_URL?: string;
|
||||
NEXT_PUBLIC_MARKETING_URL?: string;
|
||||
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
|
||||
|
||||
NEXT_PRIVATE_DATABASE_URL: string;
|
||||
|
||||
|
||||
BIN
apps/marketing/public/blog/babel.png
Normal file
BIN
apps/marketing/public/blog/babel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 467 KiB |
BIN
apps/marketing/public/blog/cal.png
Normal file
BIN
apps/marketing/public/blog/cal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 128 KiB |
BIN
apps/marketing/public/blog/cal2.png
Normal file
BIN
apps/marketing/public/blog/cal2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
BIN
apps/marketing/public/blog/dsux.png
Normal file
BIN
apps/marketing/public/blog/dsux.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 692 KiB |
Binary file not shown.
BIN
apps/marketing/public/blog/owncode.jpeg
Normal file
BIN
apps/marketing/public/blog/owncode.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
BIN
apps/marketing/public/blog/prisma.png
Normal file
BIN
apps/marketing/public/blog/prisma.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 435 KiB |
BIN
apps/marketing/public/changelog/document-visibility-demo.mp4
Normal file
BIN
apps/marketing/public/changelog/document-visibility-demo.mp4
Normal file
Binary file not shown.
BIN
apps/marketing/public/changelog/signing-order-demo.mp4
Normal file
BIN
apps/marketing/public/changelog/signing-order-demo.mp4
Normal file
Binary file not shown.
23
apps/marketing/src/app/(marketing)/[content]/content.tsx
Normal file
23
apps/marketing/src/app/(marketing)/[content]/content.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import type { DocumentTypes } from 'contentlayer/generated';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||
|
||||
const mdxComponents: MDXComponents = {
|
||||
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
||||
<Image {...props} alt={props.alt ?? ''} />
|
||||
),
|
||||
};
|
||||
|
||||
export type ContentPageContentProps = {
|
||||
document: DocumentTypes;
|
||||
};
|
||||
|
||||
export const ContentPageContent = ({ document }: ContentPageContentProps) => {
|
||||
const MDXContent = useMDXComponent(document.body.code);
|
||||
|
||||
return <MDXContent components={mdxComponents} />;
|
||||
};
|
||||
@ -1,12 +1,11 @@
|
||||
import Image from 'next/image';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
import { allDocuments } from 'contentlayer/generated';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
|
||||
import { ContentPageContent } from './content';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export const generateMetadata = ({ params }: { params: { content: string } }) => {
|
||||
@ -19,19 +18,13 @@ export const generateMetadata = ({ params }: { params: { content: string } }) =>
|
||||
return { title: document.title };
|
||||
};
|
||||
|
||||
const mdxComponents: MDXComponents = {
|
||||
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
||||
<Image {...props} alt={props.alt ?? ''} />
|
||||
),
|
||||
};
|
||||
|
||||
/**
|
||||
* A generic catch all page for the root level that checks for content layer documents.
|
||||
*
|
||||
* Will render the document if it exists, otherwise will return a 404.
|
||||
*/
|
||||
export default function ContentPage({ params }: { params: { content: string } }) {
|
||||
setupI18nSSR();
|
||||
export default async function ContentPage({ params }: { params: { content: string } }) {
|
||||
await setupI18nSSR();
|
||||
|
||||
const post = allDocuments.find((post) => post._raw.flattenedPath === params.content);
|
||||
|
||||
@ -39,11 +32,9 @@ export default function ContentPage({ params }: { params: { content: string } })
|
||||
notFound();
|
||||
}
|
||||
|
||||
const MDXContent = useMDXComponent(post.body.code);
|
||||
|
||||
return (
|
||||
<article className="prose dark:prose-invert mx-auto">
|
||||
<MDXContent components={mdxComponents} />
|
||||
<ContentPageContent document={post} />
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
23
apps/marketing/src/app/(marketing)/blog/[post]/content.tsx
Normal file
23
apps/marketing/src/app/(marketing)/blog/[post]/content.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import Image from 'next/image';
|
||||
|
||||
import type { BlogPost } from 'contentlayer/generated';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||
|
||||
const mdxComponents: MDXComponents = {
|
||||
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
||||
<Image {...props} alt={props.alt ?? ''} />
|
||||
),
|
||||
};
|
||||
|
||||
export type BlogPostContentProps = {
|
||||
post: BlogPost;
|
||||
};
|
||||
|
||||
export const BlogPostContent = ({ post }: BlogPostContentProps) => {
|
||||
const MdxContent = useMDXComponent(post.body.code);
|
||||
|
||||
return <MdxContent components={mdxComponents} />;
|
||||
};
|
||||
@ -1,16 +1,15 @@
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
import { allBlogPosts } from 'contentlayer/generated';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import type { MDXComponents } from 'mdx/types';
|
||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
|
||||
import { CallToAction } from '~/components/(marketing)/call-to-action';
|
||||
|
||||
import { BlogPostContent } from './content';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export const generateMetadata = ({ params }: { params: { post: string } }) => {
|
||||
@ -42,14 +41,8 @@ export const generateMetadata = ({ params }: { params: { post: string } }) => {
|
||||
};
|
||||
};
|
||||
|
||||
const mdxComponents: MDXComponents = {
|
||||
MdxNextImage: (props: { width: number; height: number; alt?: string; src: string }) => (
|
||||
<Image {...props} alt={props.alt ?? ''} />
|
||||
),
|
||||
};
|
||||
|
||||
export default function BlogPostPage({ params }: { params: { post: string } }) {
|
||||
setupI18nSSR();
|
||||
export default async function BlogPostPage({ params }: { params: { post: string } }) {
|
||||
await setupI18nSSR();
|
||||
|
||||
const post = allBlogPosts.find((post) => post._raw.flattenedPath === `blog/${params.post}`);
|
||||
|
||||
@ -57,8 +50,6 @@ export default function BlogPostPage({ params }: { params: { post: string } }) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const MDXContent = useMDXComponent(post.body.code);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<article className="prose dark:prose-invert mx-auto py-8">
|
||||
@ -87,7 +78,7 @@ export default function BlogPostPage({ params }: { params: { post: string } }) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MDXContent components={mdxComponents} />
|
||||
<BlogPostContent post={post} />
|
||||
|
||||
{post.tags.length > 0 && (
|
||||
<ul className="not-prose flex list-none flex-row space-x-2 px-0">
|
||||
|
||||
@ -9,8 +9,8 @@ export const metadata: Metadata = {
|
||||
title: 'Blog',
|
||||
};
|
||||
|
||||
export default function BlogPage() {
|
||||
const { i18n } = setupI18nSSR();
|
||||
export default async function BlogPage() {
|
||||
const { i18n } = await setupI18nSSR();
|
||||
|
||||
const blogPosts = allBlogPosts.sort((a, b) => {
|
||||
const dateA = new Date(a.date);
|
||||
|
||||
@ -19,10 +19,10 @@ export const TEAM_MEMBERS = [
|
||||
},
|
||||
{
|
||||
name: 'Ephraim Atta-Duncan',
|
||||
role: 'Software Engineer - Intern',
|
||||
salary: 15_000,
|
||||
role: 'Software Engineer - I',
|
||||
salary: 60_000,
|
||||
location: 'Ghana',
|
||||
engagement: msg`Part-Time`,
|
||||
engagement: msg`Full-Time`,
|
||||
joinDate: 'June 6th, 2023',
|
||||
},
|
||||
{
|
||||
|
||||
@ -131,7 +131,7 @@ const fetchEarlyAdopters = async () => {
|
||||
};
|
||||
|
||||
export default async function OpenPage() {
|
||||
setupI18nSSR();
|
||||
await setupI18nSSR();
|
||||
|
||||
const { _ } = useLingui();
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ const fontCaveat = Caveat({
|
||||
});
|
||||
|
||||
export default async function IndexPage() {
|
||||
setupI18nSSR();
|
||||
await setupI18nSSR();
|
||||
|
||||
const starCount = await fetch('https://api.github.com/repos/documenso/documenso', {
|
||||
headers: {
|
||||
|
||||
@ -30,8 +30,8 @@ export type PricingPageProps = {
|
||||
};
|
||||
};
|
||||
|
||||
export default function PricingPage() {
|
||||
setupI18nSSR();
|
||||
export default async function PricingPage() {
|
||||
await setupI18nSSR();
|
||||
|
||||
return (
|
||||
<div className="mt-6 sm:mt-12">
|
||||
|
||||
@ -163,11 +163,13 @@ export const SinglePlayerClient = () => {
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
readStatus: 'OPENED',
|
||||
rejectionReason: null,
|
||||
documentDeletedAt: null,
|
||||
signingStatus: 'NOT_SIGNED',
|
||||
sendStatus: 'NOT_SENT',
|
||||
role: 'SIGNER',
|
||||
authOptions: null,
|
||||
signingOrder: null,
|
||||
};
|
||||
|
||||
const onFileDrop = async (file: File) => {
|
||||
|
||||
@ -14,8 +14,8 @@ export const dynamic = 'force-dynamic';
|
||||
// !: This entire file is a hack to get around failed prerendering of
|
||||
// !: the Single Player Mode page. This regression was introduced during
|
||||
// !: the upgrade of Next.js to v13.5.x.
|
||||
export default function SingleplayerPage() {
|
||||
setupI18nSSR();
|
||||
export default async function SingleplayerPage() {
|
||||
await setupI18nSSR();
|
||||
|
||||
return <SinglePlayerClient />;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Suspense } from 'react';
|
||||
|
||||
import { Caveat, Inter } from 'next/font/google';
|
||||
import { cookies, headers } from 'next/headers';
|
||||
|
||||
import { AxiomWebVitals } from 'next-axiom';
|
||||
import { PublicEnvScript } from 'next-runtime-env';
|
||||
@ -10,8 +9,6 @@ import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/featur
|
||||
import { I18nClientProvider } from '@documenso/lib/client-only/providers/i18n.client';
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
import { NEXT_PUBLIC_MARKETING_URL } from '@documenso/lib/constants/app';
|
||||
import type { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
|
||||
import { ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n';
|
||||
import { getAllAnonymousFlags } from '@documenso/lib/universal/get-feature-flag';
|
||||
import { TrpcProvider } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
@ -59,25 +56,7 @@ export function generateMetadata() {
|
||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
const flags = await getAllAnonymousFlags();
|
||||
|
||||
let overrideLang: (typeof SUPPORTED_LANGUAGE_CODES)[number] | undefined;
|
||||
|
||||
// Should be safe to remove when we upgrade NextJS.
|
||||
// https://github.com/vercel/next.js/pull/65008
|
||||
// Currently if the middleware sets the cookie, it's not accessible in the cookies
|
||||
// during the same render.
|
||||
// So we go the roundabout way of checking the header for the set-cookie value.
|
||||
if (!cookies().get('i18n')) {
|
||||
const setCookieValue = headers().get('set-cookie');
|
||||
const i18nCookie = setCookieValue?.split(';').find((cookie) => cookie.startsWith('i18n='));
|
||||
|
||||
if (i18nCookie) {
|
||||
const i18n = i18nCookie.split('=')[1];
|
||||
|
||||
overrideLang = ZSupportedLanguageCodeSchema.parse(i18n);
|
||||
}
|
||||
}
|
||||
|
||||
const { lang, i18n } = setupI18nSSR(overrideLang);
|
||||
const { lang, locales, i18n } = await setupI18nSSR();
|
||||
|
||||
return (
|
||||
<html
|
||||
@ -105,7 +84,10 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<PlausibleProvider>
|
||||
<TrpcProvider>
|
||||
<I18nClientProvider initialLocale={lang} initialMessages={i18n.messages}>
|
||||
<I18nClientProvider
|
||||
initialLocaleData={{ lang, locales }}
|
||||
initialMessages={i18n.messages}
|
||||
>
|
||||
{children}
|
||||
</I18nClientProvider>
|
||||
</TrpcProvider>
|
||||
|
||||
@ -108,14 +108,21 @@ export const Carousel = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedIndex(emblaApi.selectedScrollSnap());
|
||||
emblaThumbsApi.scrollTo(emblaApi.selectedScrollSnap());
|
||||
const newIndex = emblaApi.selectedScrollSnap();
|
||||
|
||||
setSelectedIndex(newIndex);
|
||||
emblaThumbsApi.scrollTo(newIndex);
|
||||
|
||||
resetProgress();
|
||||
|
||||
const currentVideo = videoRefs.current[newIndex];
|
||||
if (currentVideo) {
|
||||
currentVideo.currentTime = 0;
|
||||
}
|
||||
|
||||
// moduleResolution: bundler breaks this type
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const autoplay = emblaApi.plugins()?.autoplay as unknown as AutoplayType | undefined;
|
||||
const autoplay = emblaApi?.plugins()?.autoplay as unknown as AutoplayType | undefined;
|
||||
|
||||
if (autoplay) {
|
||||
autoplay.reset();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { type HTMLAttributes, useState } from 'react';
|
||||
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
@ -9,15 +9,15 @@ import { msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { FaXTwitter } from 'react-icons/fa6';
|
||||
import { LiaDiscord } from 'react-icons/lia';
|
||||
import { LuGithub } from 'react-icons/lu';
|
||||
import { LuGithub, LuLanguages } from 'react-icons/lu';
|
||||
|
||||
import LogoImage from '@documenso/assets/logo.png';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { ThemeSwitcher } from '@documenso/ui/primitives/theme-switcher';
|
||||
|
||||
import { I18nSwitcher } from '~/components/(marketing)/i18n-switcher';
|
||||
|
||||
import { SUPPORTED_LANGUAGES } from '@documenso/lib/constants/i18n';
|
||||
// import { StatusWidgetContainer } from './status-widget-container';
|
||||
import { LanguageSwitcherDialog } from '@documenso/ui/components/common/language-switcher-dialog';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { ThemeSwitcher } from '@documenso/ui/primitives/theme-switcher';
|
||||
|
||||
export type FooterProps = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
@ -44,7 +44,9 @@ const FOOTER_LINKS = [
|
||||
];
|
||||
|
||||
export const Footer = ({ className, ...props }: FooterProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { _, i18n } = useLingui();
|
||||
|
||||
const [languageSwitcherOpen, setLanguageSwitcherOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className={cn('border-t py-12', className)} {...props}>
|
||||
@ -97,13 +99,22 @@ export const Footer = ({ className, ...props }: FooterProps) => {
|
||||
</p>
|
||||
|
||||
<div className="flex flex-row-reverse items-center sm:flex-row">
|
||||
<I18nSwitcher className="text-muted-foreground ml-2 rounded-full font-normal sm:mr-2" />
|
||||
<Button
|
||||
className="text-muted-foreground ml-2 rounded-full font-normal sm:mr-2"
|
||||
variant="ghost"
|
||||
onClick={() => setLanguageSwitcherOpen(true)}
|
||||
>
|
||||
<LuLanguages className="mr-1.5 h-4 w-4" />
|
||||
{SUPPORTED_LANGUAGES[i18n.locale]?.full || i18n.locale}
|
||||
</Button>
|
||||
|
||||
<div className="flex flex-wrap">
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LanguageSwitcherDialog open={languageSwitcherOpen} setOpen={setLanguageSwitcherOpen} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { CheckIcon } from 'lucide-react';
|
||||
import { LuLanguages } from 'react-icons/lu';
|
||||
|
||||
import { SUPPORTED_LANGUAGES } from '@documenso/lib/constants/i18n';
|
||||
import { switchI18NLanguage } from '@documenso/lib/server-only/i18n/switch-i18n-language';
|
||||
import { dynamicActivate } from '@documenso/lib/utils/i18n';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
CommandDialog,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@documenso/ui/primitives/command';
|
||||
|
||||
type I18nSwitcherProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const I18nSwitcher = ({ className }: I18nSwitcherProps) => {
|
||||
const { i18n, _ } = useLingui();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [value, setValue] = useState(i18n.locale);
|
||||
|
||||
const setLanguage = async (lang: string) => {
|
||||
setValue(lang);
|
||||
setOpen(false);
|
||||
|
||||
await dynamicActivate(i18n, lang);
|
||||
await switchI18NLanguage(lang);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button className={className} variant="ghost" onClick={() => setOpen(true)}>
|
||||
<LuLanguages className="mr-1.5 h-4 w-4" />
|
||||
{SUPPORTED_LANGUAGES[value]?.full || i18n.locale}
|
||||
</Button>
|
||||
|
||||
<CommandDialog open={open} onOpenChange={setOpen}>
|
||||
<CommandInput placeholder={_(msg`Search languages...`)} />
|
||||
|
||||
<CommandList>
|
||||
<CommandGroup>
|
||||
{Object.values(SUPPORTED_LANGUAGES).map((language) => (
|
||||
<CommandItem
|
||||
key={language.short}
|
||||
value={language.full}
|
||||
onSelect={async () => setLanguage(language.short)}
|
||||
>
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
'mr-2 h-4 w-4',
|
||||
value === language.short ? 'opacity-100' : 'opacity-0',
|
||||
)}
|
||||
/>
|
||||
{SUPPORTED_LANGUAGES[language.short].full}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</CommandDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,39 +0,0 @@
|
||||
import { cookies } from 'next/headers';
|
||||
import type { NextRequest } from 'next/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
import { extractSupportedLanguage } from '@documenso/lib/utils/i18n';
|
||||
|
||||
export default function middleware(req: NextRequest) {
|
||||
const lang = extractSupportedLanguage({
|
||||
headers: req.headers,
|
||||
cookies: cookies(),
|
||||
});
|
||||
|
||||
const response = NextResponse.next();
|
||||
|
||||
response.cookies.set('i18n', lang);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
/*
|
||||
* Match all request paths except for the ones starting with:
|
||||
* - api (API routes)
|
||||
* - _next/static (static files)
|
||||
* - _next/image (image optimization files)
|
||||
* - favicon.ico (favicon file)
|
||||
* - ingest (analytics)
|
||||
* - site.webmanifest
|
||||
*/
|
||||
{
|
||||
source: '/((?!api|_next/static|_next/image|ingest|favicon|site.webmanifest).*)',
|
||||
missing: [
|
||||
{ type: 'header', key: 'next-router-prefetch' },
|
||||
{ type: 'header', key: 'purpose', value: 'prefetch' },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user