mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 17:21:41 +10:00
Merge branch 'feat/refresh' into fix/467-bugsafari-only-unable-to-copy-document-sharing-link
This commit is contained in:
126
CODE_OF_CONDUCT.md
Normal file
126
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||||
|
identity and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
- Demonstrating empathy and kindness toward other people
|
||||||
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
- Giving and gracefully accepting constructive feedback
|
||||||
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
- Focusing on what is best not just for us as individuals, but for the overall
|
||||||
|
community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
|
any kind
|
||||||
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or email address,
|
||||||
|
without their explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
support@documenso.com.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of
|
||||||
|
actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or permanent
|
||||||
|
ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||||
|
community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.1, available at
|
||||||
|
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by
|
||||||
|
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||||
|
[https://www.contributor-covenant.org/translations][translations].
|
||||||
@ -5,20 +5,36 @@ If you plan to contribute to Documenso, please take a moment to feel awesome ✨
|
|||||||
## Before getting started
|
## Before getting started
|
||||||
|
|
||||||
- Before jumping into a PR be sure to search [existing PRs](https://github.com/documenso/documenso/pulls) or [issues](https://github.com/documenso/documenso/issues) for an open or closed item that relates to your submission.
|
- Before jumping into a PR be sure to search [existing PRs](https://github.com/documenso/documenso/pulls) or [issues](https://github.com/documenso/documenso/issues) for an open or closed item that relates to your submission.
|
||||||
- Select and issue from [here](https://github.com/documenso/documenso/issues) or create a new one
|
- Select an issue from [here](https://github.com/documenso/documenso/issues) or create a new one
|
||||||
- Consider the results from the discussion in the issue
|
- Consider the results from the discussion on the issue
|
||||||
- Accept the [Contributor License Agreement](https://documen.so/cla) to ensure we can accept your contributions.
|
- Accept the [Contributor License Agreement](https://documen.so/cla) to ensure we can accept your contributions.
|
||||||
|
|
||||||
|
## Taking issues
|
||||||
|
|
||||||
|
Before taking an issue, ensure that:
|
||||||
|
|
||||||
|
- The issue has been assigned the public label
|
||||||
|
- The issue is clearly defined and understood
|
||||||
|
- No one has been assigned to the issue
|
||||||
|
- No one has expressed intention to work on it
|
||||||
|
|
||||||
|
You can then:
|
||||||
|
|
||||||
|
1. Comment on the issue with your intention to work on it
|
||||||
|
2. Begin work on the issue
|
||||||
|
|
||||||
|
Always feel free to ask questions or seek clarification on the issue.
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
The development branch is <code>main</code>. All pull request should be made against this branch. If you need help getting started, [join us on Discord](https://documen.so/discord).
|
The development branch is <code>main</code>. All pull requests should be made against this branch. If you need help getting started, [join us on Discord](https://documen.so/discord).
|
||||||
|
|
||||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your
|
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your
|
||||||
own GitHub account and then
|
own GitHub account and then
|
||||||
[clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
|
[clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
|
||||||
2. Create a new branch:
|
2. Create a new branch:
|
||||||
|
|
||||||
- Create a new branch (include the issue id and somthing readable):
|
- Create a new branch (include the issue id and something readable):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git checkout -b doc-999-my-feature-or-fix
|
git checkout -b doc-999-my-feature-or-fix
|
||||||
@ -29,7 +45,7 @@ The development branch is <code>main</code>. All pull request should be made aga
|
|||||||
## Building
|
## Building
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
> Please be sure that you can make a full production build before pushing code or creating PRs.
|
> Please ensure you can make a full production build before pushing code or creating PRs.
|
||||||
|
|
||||||
You can build the project with:
|
You can build the project with:
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
<a href="https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/documenso/documenso">
|
<a href="https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/documenso/documenso">
|
||||||
<img alt="open in devcontainer" src="https://img.shields.io/static/v1?label=Dev%20Containers&message=Enabled&color=blue&logo=visualstudiocode" />
|
<img alt="open in devcontainer" src="https://img.shields.io/static/v1?label=Dev%20Containers&message=Enabled&color=blue&logo=visualstudiocode" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href="code_of_conduct.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> 🦺 Documenso 1.0 is deployed to our <a href="https://documen.so/staging" target="_blank">Staging Environment</a>.
|
> 🦺 Documenso 1.0 is deployed to our <a href="https://documen.so/staging" target="_blank">Staging Environment</a>.
|
||||||
@ -130,14 +131,12 @@ git clone https://github.com/documenso/documenso
|
|||||||
|
|
||||||
2. Set up your `.env` file using the recommendations in the `.env.example` file. Alternatively just run `cp .env.example .env` to get started with our handpicked defaults.
|
2. Set up your `.env` file using the recommendations in the `.env.example` file. Alternatively just run `cp .env.example .env` to get started with our handpicked defaults.
|
||||||
|
|
||||||
|
|
||||||
3. Run `npm run dx` in the root directory
|
3. Run `npm run dx` in the root directory
|
||||||
|
|
||||||
- This will spin up a postgres database and inbucket mailserver in a docker container.
|
- This will spin up a postgres database and inbucket mailserver in a docker container.
|
||||||
|
|
||||||
4. Run `npm run dev` in the root directory
|
4. Run `npm run dev` in the root directory
|
||||||
|
|
||||||
|
|
||||||
5. Want it even faster? Just use
|
5. Want it even faster? Just use
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -170,6 +169,7 @@ git clone https://github.com/documenso/documenso
|
|||||||
3. Create your `.env` from the `.env.example`. You can use `cp .env.example .env` to get started with our handpicked defaults.
|
3. Create your `.env` from the `.env.example`. You can use `cp .env.example .env` to get started with our handpicked defaults.
|
||||||
|
|
||||||
4. Set the following environement variables.
|
4. Set the following environement variables.
|
||||||
|
|
||||||
- NEXTAUTH_URL
|
- NEXTAUTH_URL
|
||||||
- NEXTAUTH_SECRET
|
- NEXTAUTH_SECRET
|
||||||
- NEXT_PUBLIC_WEBAPP_URL
|
- NEXT_PUBLIC_WEBAPP_URL
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
"react-confetti": "^6.1.0",
|
"react-confetti": "^6.1.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.11.0",
|
||||||
"recharts": "^2.7.2",
|
"recharts": "^2.7.2",
|
||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.1.6",
|
||||||
|
|||||||
@ -31,6 +31,22 @@ export const TEAM_MEMBERS = [
|
|||||||
engagement: 'Full-Time',
|
engagement: 'Full-Time',
|
||||||
joinDate: 'July 26th, 2023',
|
joinDate: 'July 26th, 2023',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Catalin-Marinel Pit',
|
||||||
|
role: 'Software Engineer - II',
|
||||||
|
salary: 80_000,
|
||||||
|
location: 'Romania',
|
||||||
|
engagement: 'Full-Time',
|
||||||
|
joinDate: 'September 4th, 2023',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Gowdhama Rajan B',
|
||||||
|
role: 'Designer - III',
|
||||||
|
salary: 100_000,
|
||||||
|
location: 'India',
|
||||||
|
engagement: 'Full-Time',
|
||||||
|
joinDate: 'October 9th, 2023',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const FUNDING_RAISED = [
|
export const FUNDING_RAISED = [
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Github } from 'lucide-react';
|
|
||||||
import { usePlausible } from 'next-plausible';
|
import { usePlausible } from 'next-plausible';
|
||||||
|
import { LuGithub } from 'react-icons/lu';
|
||||||
|
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ export const Callout = ({ starCount }: CalloutProps) => {
|
|||||||
onClick={() => event('view-github')}
|
onClick={() => event('view-github')}
|
||||||
>
|
>
|
||||||
<Button variant="outline" className="rounded-full bg-transparent backdrop-blur-sm">
|
<Button variant="outline" className="rounded-full bg-transparent backdrop-blur-sm">
|
||||||
<Github className="mr-2 h-5 w-5" />
|
<LuGithub className="mr-2 h-5 w-5" />
|
||||||
Star on Github
|
Star on Github
|
||||||
{starCount && starCount > 0 && (
|
{starCount && starCount > 0 && (
|
||||||
<span className="bg-primary dark:text-background -mr-2.5 ml-2.5 rounded-full px-2 py-1.5 text-xs">
|
<span className="bg-primary dark:text-background -mr-2.5 ml-2.5 rounded-full px-2 py-1.5 text-xs">
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
@ -55,8 +55,8 @@ export const ClaimPlanDialog = ({ className, planId, children }: ClaimPlanDialog
|
|||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
|
reset,
|
||||||
} = useForm<TClaimPlanDialogFormSchema>({
|
} = useForm<TClaimPlanDialogFormSchema>({
|
||||||
mode: 'onBlur',
|
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: params?.get('name') ?? '',
|
name: params?.get('name') ?? '',
|
||||||
email: params?.get('email') ?? '',
|
email: params?.get('email') ?? '',
|
||||||
@ -91,6 +91,12 @@ export const ClaimPlanDialog = ({ className, planId, children }: ClaimPlanDialog
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isSubmitting && !open) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={(value) => !isSubmitting && setOpen(value)}>
|
<Dialog open={open} onOpenChange={(value) => !isSubmitting && setOpen(value)}>
|
||||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||||
|
|||||||
@ -5,17 +5,20 @@ import { HTMLAttributes } from 'react';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Github, MessagesSquare, Moon, Sun, Twitter } from 'lucide-react';
|
import { Moon, Sun } from 'lucide-react';
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
|
import { FaXTwitter } from 'react-icons/fa6';
|
||||||
|
import { LiaDiscord } from 'react-icons/lia';
|
||||||
|
import { LuGithub } from 'react-icons/lu';
|
||||||
|
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
|
|
||||||
export type FooterProps = HTMLAttributes<HTMLDivElement>;
|
export type FooterProps = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
const SOCIAL_LINKS = [
|
const SOCIAL_LINKS = [
|
||||||
{ href: 'https://twitter.com/documenso', icon: <Twitter className="h-6 w-6" /> },
|
{ href: 'https://twitter.com/documenso', icon: <FaXTwitter className="h-6 w-6" /> },
|
||||||
{ href: 'https://github.com/documenso/documenso', icon: <Github className="h-6 w-6" /> },
|
{ href: 'https://github.com/documenso/documenso', icon: <LuGithub className="h-6 w-6" /> },
|
||||||
{ href: 'https://documen.so/discord', icon: <MessagesSquare className="h-6 w-6" /> },
|
{ href: 'https://documen.so/discord', icon: <LiaDiscord className="h-7 w-7" /> },
|
||||||
];
|
];
|
||||||
|
|
||||||
const FOOTER_LINKS = [
|
const FOOTER_LINKS = [
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Variants, motion } from 'framer-motion';
|
import { Variants, motion } from 'framer-motion';
|
||||||
import { Github } from 'lucide-react';
|
|
||||||
import { usePlausible } from 'next-plausible';
|
import { usePlausible } from 'next-plausible';
|
||||||
|
import { LuGithub } from 'react-icons/lu';
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||||
@ -122,7 +122,7 @@ export const Hero = ({ className, ...props }: HeroProps) => {
|
|||||||
|
|
||||||
<Link href="https://github.com/documenso/documenso" onClick={() => event('view-github')}>
|
<Link href="https://github.com/documenso/documenso" onClick={() => event('view-github')}>
|
||||||
<Button variant="outline" className="rounded-full bg-transparent backdrop-blur-sm">
|
<Button variant="outline" className="rounded-full bg-transparent backdrop-blur-sm">
|
||||||
<Github className="mr-2 h-5 w-5" />
|
<LuGithub className="mr-2 h-5 w-5" />
|
||||||
Star on Github
|
Star on Github
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import Image from 'next/image';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { motion, useReducedMotion } from 'framer-motion';
|
import { motion, useReducedMotion } from 'framer-motion';
|
||||||
import { Github, MessagesSquare, Twitter } from 'lucide-react';
|
import { FaXTwitter } from 'react-icons/fa6';
|
||||||
|
import { LiaDiscord } from 'react-icons/lia';
|
||||||
|
import { LuGithub } from 'react-icons/lu';
|
||||||
|
|
||||||
import { Sheet, SheetContent } from '@documenso/ui/primitives/sheet';
|
import { Sheet, SheetContent } from '@documenso/ui/primitives/sheet';
|
||||||
|
|
||||||
@ -111,7 +113,7 @@ export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigat
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
className="text-foreground hover:text-foreground/80"
|
className="text-foreground hover:text-foreground/80"
|
||||||
>
|
>
|
||||||
<Twitter className="h-6 w-6" />
|
<FaXTwitter className="h-6 w-6" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
@ -119,7 +121,7 @@ export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigat
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
className="text-foreground hover:text-foreground/80"
|
className="text-foreground hover:text-foreground/80"
|
||||||
>
|
>
|
||||||
<Github className="h-6 w-6" />
|
<LuGithub className="h-6 w-6" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
@ -127,7 +129,7 @@ export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigat
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
className="text-foreground hover:text-foreground/80"
|
className="text-foreground hover:text-foreground/80"
|
||||||
>
|
>
|
||||||
<MessagesSquare className="h-6 w-6" />
|
<LiaDiscord className="h-7 w-7" />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
|
|||||||
@ -391,6 +391,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => {
|
|||||||
|
|
||||||
<SignaturePad
|
<SignaturePad
|
||||||
className="aspect-video w-full rounded-md border"
|
className="aspect-video w-full rounded-md border"
|
||||||
|
defaultValue={signatureDataUrl || ''}
|
||||||
onChange={setDraftSignatureDataUrl}
|
onChange={setDraftSignatureDataUrl}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.11.0",
|
||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.1",
|
||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
|
|||||||
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
import { HTMLAttributes, useState } from 'react';
|
import { HTMLAttributes, useState } from 'react';
|
||||||
|
|
||||||
import { Copy, Share, Twitter } from 'lucide-react';
|
import { Copy, Share } from 'lucide-react';
|
||||||
|
import { FaXTwitter } from 'react-icons/fa6';
|
||||||
|
|
||||||
import { useCopyShareLink } from '@documenso/lib/client-only/hooks/use-copy-share-link';
|
import { useCopyShareLink } from '@documenso/lib/client-only/hooks/use-copy-share-link';
|
||||||
import { generateTwitterIntent } from '@documenso/lib/universal/generate-twitter-intent';
|
import { generateTwitterIntent } from '@documenso/lib/universal/generate-twitter-intent';
|
||||||
@ -113,7 +114,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" className="mt-4" onClick={onTweetClick}>
|
<Button variant="outline" className="mt-4" onClick={onTweetClick}>
|
||||||
<Twitter className="mr-2 h-4 w-4" />
|
<FaXTwitter className="mr-2 h-4 w-4" />
|
||||||
Tweet
|
Tweet
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import Link from 'next/link';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
CreditCard,
|
CreditCard,
|
||||||
Github,
|
|
||||||
Key,
|
Key,
|
||||||
LogOut,
|
LogOut,
|
||||||
User as LucideUser,
|
User as LucideUser,
|
||||||
@ -16,6 +15,7 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { signOut } from 'next-auth/react';
|
import { signOut } from 'next-auth/react';
|
||||||
import { useTheme } from 'next-themes';
|
import { useTheme } from 'next-themes';
|
||||||
|
import { LuGithub } from 'react-icons/lu';
|
||||||
|
|
||||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||||
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
||||||
@ -130,7 +130,7 @@ export const ProfileDropdown = ({ user }: ProfileDropdownProps) => {
|
|||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link href="https://github.com/documenso/documenso" className="cursor-pointer">
|
<Link href="https://github.com/documenso/documenso" className="cursor-pointer">
|
||||||
<Github className="mr-2 h-4 w-4" />
|
<LuGithub className="mr-2 h-4 w-4" />
|
||||||
Star on Github
|
Star on Github
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { FormErrorMessage } from '../form/form-error-message';
|
|||||||
|
|
||||||
export const ZPasswordFormSchema = z
|
export const ZPasswordFormSchema = z
|
||||||
.object({
|
.object({
|
||||||
|
currentPassword: z.string().min(6).max(72),
|
||||||
password: z.string().min(6).max(72),
|
password: z.string().min(6).max(72),
|
||||||
repeatedPassword: z.string().min(6).max(72),
|
repeatedPassword: z.string().min(6).max(72),
|
||||||
})
|
})
|
||||||
@ -40,6 +41,7 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
|
|||||||
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
const [showCurrentPassword, setShowCurrentPassword] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -48,6 +50,7 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
|
|||||||
formState: { errors, isSubmitting },
|
formState: { errors, isSubmitting },
|
||||||
} = useForm<TPasswordFormSchema>({
|
} = useForm<TPasswordFormSchema>({
|
||||||
values: {
|
values: {
|
||||||
|
currentPassword: '',
|
||||||
password: '',
|
password: '',
|
||||||
repeatedPassword: '',
|
repeatedPassword: '',
|
||||||
},
|
},
|
||||||
@ -56,9 +59,10 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
|
|||||||
|
|
||||||
const { mutateAsync: updatePassword } = trpc.profile.updatePassword.useMutation();
|
const { mutateAsync: updatePassword } = trpc.profile.updatePassword.useMutation();
|
||||||
|
|
||||||
const onFormSubmit = async ({ password }: TPasswordFormSchema) => {
|
const onFormSubmit = async ({ currentPassword, password }: TPasswordFormSchema) => {
|
||||||
try {
|
try {
|
||||||
await updatePassword({
|
await updatePassword({
|
||||||
|
currentPassword,
|
||||||
password,
|
password,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -92,6 +96,39 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
|
|||||||
className={cn('flex w-full flex-col gap-y-4', className)}
|
className={cn('flex w-full flex-col gap-y-4', className)}
|
||||||
onSubmit={handleSubmit(onFormSubmit)}
|
onSubmit={handleSubmit(onFormSubmit)}
|
||||||
>
|
>
|
||||||
|
<div>
|
||||||
|
<Label htmlFor="current-password" className="text-muted-foreground">
|
||||||
|
Current Password
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
id="current-password"
|
||||||
|
type={showCurrentPassword ? 'text' : 'password'}
|
||||||
|
minLength={6}
|
||||||
|
maxLength={72}
|
||||||
|
autoComplete="current-password"
|
||||||
|
className="bg-background mt-2 pr-10"
|
||||||
|
{...register('currentPassword')}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
type="button"
|
||||||
|
className="absolute right-0 top-0 flex h-full items-center justify-center pr-3"
|
||||||
|
aria-label={showCurrentPassword ? 'Mask password' : 'Reveal password'}
|
||||||
|
onClick={() => setShowCurrentPassword((show) => !show)}
|
||||||
|
>
|
||||||
|
{showCurrentPassword ? (
|
||||||
|
<EyeOff className="text-muted-foreground h-5 w-5" />
|
||||||
|
) : (
|
||||||
|
<Eye className="text-muted-foreground h-5 w-5" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormErrorMessage className="mt-1.5" error={errors.currentPassword} />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="password" className="text-muted-foreground">
|
<Label htmlFor="password" className="text-muted-foreground">
|
||||||
Password
|
Password
|
||||||
|
|||||||
10
package-lock.json
generated
10
package-lock.json
generated
@ -54,7 +54,7 @@
|
|||||||
"react-confetti": "^6.1.0",
|
"react-confetti": "^6.1.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.11.0",
|
||||||
"recharts": "^2.7.2",
|
"recharts": "^2.7.2",
|
||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"typescript": "5.1.6",
|
"typescript": "5.1.6",
|
||||||
@ -95,7 +95,7 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-hook-form": "^7.43.9",
|
"react-hook-form": "^7.43.9",
|
||||||
"react-icons": "^4.8.0",
|
"react-icons": "^4.11.0",
|
||||||
"react-rnd": "^10.4.1",
|
"react-rnd": "^10.4.1",
|
||||||
"sharp": "0.32.5",
|
"sharp": "0.32.5",
|
||||||
"ts-pattern": "^5.0.5",
|
"ts-pattern": "^5.0.5",
|
||||||
@ -16359,9 +16359,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-icons": {
|
"node_modules/react-icons": {
|
||||||
"version": "4.10.1",
|
"version": "4.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz",
|
||||||
"integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==",
|
"integrity": "sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*"
|
"react": "*"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,14 @@ import { SALT_ROUNDS } from '../../constants/auth';
|
|||||||
export type UpdatePasswordOptions = {
|
export type UpdatePasswordOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
password: string;
|
password: string;
|
||||||
|
currentPassword: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updatePassword = async ({ userId, password }: UpdatePasswordOptions) => {
|
export const updatePassword = async ({
|
||||||
|
userId,
|
||||||
|
password,
|
||||||
|
currentPassword,
|
||||||
|
}: UpdatePasswordOptions) => {
|
||||||
// Existence check
|
// Existence check
|
||||||
const user = await prisma.user.findFirstOrThrow({
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
where: {
|
where: {
|
||||||
@ -17,23 +22,29 @@ export const updatePassword = async ({ userId, password }: UpdatePasswordOptions
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const hashedPassword = await hash(password, SALT_ROUNDS);
|
if (!user.password) {
|
||||||
|
throw new Error('User has no password');
|
||||||
|
}
|
||||||
|
|
||||||
|
const isCurrentPasswordValid = await compare(currentPassword, user.password);
|
||||||
|
if (!isCurrentPasswordValid) {
|
||||||
|
throw new Error('Current password is incorrect.');
|
||||||
|
}
|
||||||
|
|
||||||
if (user.password) {
|
|
||||||
// Compare the new password with the old password
|
// Compare the new password with the old password
|
||||||
const isSamePassword = await compare(password, user.password);
|
const isSamePassword = await compare(password, user.password);
|
||||||
|
|
||||||
if (isSamePassword) {
|
if (isSamePassword) {
|
||||||
throw new Error('Your new password cannot be the same as your old password.');
|
throw new Error('Your new password cannot be the same as your old password.');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const hashedNewPassword = await hash(password, SALT_ROUNDS);
|
||||||
|
|
||||||
const updatedUser = await prisma.user.update({
|
const updatedUser = await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
id: userId,
|
id: userId,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
password: hashedPassword,
|
password: hashedNewPassword,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -12,12 +12,16 @@ export const authRouter = router({
|
|||||||
|
|
||||||
return await createUser({ name, email, password, signature });
|
return await createUser({ name, email, password, signature });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
let message =
|
||||||
|
'We were unable to create your account. Please review the information you provided and try again.';
|
||||||
|
|
||||||
|
if (err instanceof Error && err.message === 'User already exists') {
|
||||||
|
message = 'User with this email already exists. Please use a different email address.';
|
||||||
|
}
|
||||||
|
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: 'BAD_REQUEST',
|
code: 'BAD_REQUEST',
|
||||||
message:
|
message,
|
||||||
'We were unable to create your account. Please review the information you provided and try again.',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -40,11 +40,12 @@ export const profileRouter = router({
|
|||||||
.input(ZUpdatePasswordMutationSchema)
|
.input(ZUpdatePasswordMutationSchema)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
const { password } = input;
|
const { password, currentPassword } = input;
|
||||||
|
|
||||||
return await updatePassword({
|
return await updatePassword({
|
||||||
userId: ctx.user.id,
|
userId: ctx.user.id,
|
||||||
password,
|
password,
|
||||||
|
currentPassword,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
let message =
|
let message =
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export const ZUpdateProfileMutationSchema = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const ZUpdatePasswordMutationSchema = z.object({
|
export const ZUpdatePasswordMutationSchema = z.object({
|
||||||
|
currentPassword: z.string().min(6),
|
||||||
password: z.string().min(6),
|
password: z.string().min(6),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -425,7 +425,7 @@ export const AddFieldsFormPartial = ({
|
|||||||
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
|
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
|
||||||
<p
|
<p
|
||||||
className={cn(
|
className={cn(
|
||||||
'text-muted-foreground group-data-[selected]:text-foreground text-3xl font-medium',
|
'text-muted-foreground group-data-[selected]:text-foreground w-full truncate text-3xl font-medium',
|
||||||
fontCaveat.className,
|
fontCaveat.className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -97,10 +97,7 @@ export const DocumentFlowFormContainerStep = ({
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-muted-foreground text-sm">
|
<p className="text-muted-foreground text-sm">
|
||||||
{title}{' '}
|
Step <span>{`${step} of ${maxStep}`}</span>
|
||||||
<span>
|
|
||||||
({step}/{maxStep})
|
|
||||||
</span>
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="bg-muted relative mt-4 h-[2px] rounded-md">
|
<div className="bg-muted relative mt-4 h-[2px] rounded-md">
|
||||||
|
|||||||
Reference in New Issue
Block a user