@wasp.sh/wasp-cli-darwin-arm64-unknown 0.20.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/data/Cli/starters/basic/README.md +1 -1
- package/data/Cli/starters/basic/package.json +10 -7
- package/data/Cli/starters/basic/src/App.css +30 -12
- package/data/Cli/starters/basic/src/App.tsx +1 -1
- package/data/Cli/starters/basic/src/auth/AuthLayout.tsx +3 -1
- package/data/Cli/starters/basic/src/auth/email/EmailVerificationPage.tsx +2 -2
- package/data/Cli/starters/basic/src/auth/email/LoginPage.tsx +3 -3
- package/data/Cli/starters/basic/src/auth/email/PasswordResetPage.tsx +2 -2
- package/data/Cli/starters/basic/src/auth/email/SignupPage.tsx +2 -2
- package/data/Cli/starters/basic/src/shared/components/Button.tsx +3 -2
- package/data/Cli/starters/basic/src/shared/components/Dialog.tsx +23 -41
- package/data/Cli/starters/basic/src/shared/components/Header.tsx +2 -2
- package/data/Cli/starters/basic/src/shared/components/Input.tsx +25 -28
- package/data/Cli/starters/basic/src/tags/components/ColorRadioButton.tsx +8 -9
- package/data/Cli/starters/basic/src/tags/components/CreateTagDialog.tsx +29 -29
- package/data/Cli/starters/basic/src/tags/components/CreateTagForm.tsx +6 -8
- package/data/Cli/starters/basic/src/tasks/components/CreateTaskForm.tsx +2 -2
- package/data/Cli/starters/basic/src/tasks/components/TaskListItem.tsx +3 -2
- package/data/Cli/starters/basic/vite.config.ts +3 -0
- package/data/Cli/starters/minimal/package.json +2 -3
- package/data/Cli/starters/minimal/vite.config.ts +2 -0
- package/data/Cli/starters/skeleton/src/vite-env.d.ts +2 -6
- package/data/Generator/libs/auth/wasp.sh-lib-auth-0.21.0.tgz +0 -0
- package/data/Generator/templates/Dockerfile +13 -12
- package/data/Generator/templates/sdk/wasp/auth/forms/Auth.tsx +2 -11
- package/data/Generator/templates/sdk/wasp/auth/forms/internal/common/LoginSignupForm.tsx +10 -4
- package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ForgotPasswordForm.tsx +4 -3
- package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ResetPasswordForm.tsx +4 -4
- package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/VerifyEmailForm.tsx +3 -4
- package/data/Generator/templates/sdk/wasp/auth/forms/internal/social/SocialIcons.tsx +11 -0
- package/data/Generator/templates/sdk/wasp/auth/forms/types.ts +0 -6
- package/data/Generator/templates/sdk/wasp/auth/jwt.ts +9 -16
- package/data/Generator/templates/sdk/wasp/auth/password.ts +1 -36
- package/data/Generator/templates/sdk/wasp/auth/utils.ts +2 -0
- package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/DefaultRootErrorBoundary.tsx +1 -1
- package/data/Generator/templates/sdk/wasp/client/app/components/WaspApp.tsx +45 -0
- package/data/Generator/templates/sdk/wasp/client/app/index.tsx +26 -0
- package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/OAuthCallback.tsx +9 -8
- package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/createAuthRequiredPage.jsx +7 -9
- package/data/Generator/templates/sdk/wasp/client/app/router/router.tsx +47 -0
- package/data/Generator/templates/sdk/wasp/client/auth/index.ts +3 -0
- package/data/Generator/templates/sdk/wasp/client/auth/microsoft.ts +2 -0
- package/data/Generator/templates/sdk/wasp/client/auth/ui.ts +3 -0
- package/data/Generator/templates/sdk/wasp/client/router/Link.tsx +2 -2
- package/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx +10 -12
- package/data/Generator/templates/sdk/wasp/client/vite/index.ts +1 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/detectServerImports.ts +50 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/envFile.ts +112 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/build.ts +38 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/dev.ts +35 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/typescriptCheck.ts +32 -0
- package/data/Generator/templates/{react-app/vite → sdk/wasp/client/vite/plugins}/validateEnv.ts +17 -7
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/virtualModules.ts +29 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/wasp.ts +36 -0
- package/data/Generator/templates/sdk/wasp/client/vite/plugins/waspConfig.ts +56 -0
- package/data/Generator/templates/{react-app → sdk/wasp/client/vite/virtual-files/files}/index.html +1 -2
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/index.tsx +34 -0
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx +17 -0
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/index.ts +20 -0
- package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/resolver.ts +28 -0
- package/data/Generator/templates/sdk/wasp/package.json +6 -4
- package/data/Generator/templates/sdk/wasp/scripts/copy-assets.js +67 -7
- package/data/Generator/templates/sdk/wasp/server/auth/hooks.ts +3 -0
- package/data/Generator/templates/sdk/wasp/server/auth/oauth/index.ts +4 -0
- package/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/microsoft.ts +23 -0
- package/data/Generator/templates/sdk/wasp/server/auth/user.ts +6 -0
- package/data/Generator/templates/sdk/wasp/server/env.ts +11 -0
- package/data/Generator/templates/sdk/wasp/tsconfig.json +5 -1
- package/data/Generator/templates/server/src/auth/providers/config/microsoft.ts +65 -0
- package/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts +1 -1
- package/data/packages/deploy/dist/common/clientApp.js +4 -11
- package/data/packages/deploy/dist/common/terminal.js +7 -0
- package/data/packages/deploy/dist/common/waspProject.js +11 -7
- package/data/packages/deploy/dist/providers/fly/CommonOps.js +3 -3
- package/data/packages/deploy/dist/providers/fly/commands/cmd/cmd.js +1 -1
- package/data/packages/deploy/dist/providers/fly/commands/deploy/deploy.js +6 -5
- package/data/packages/deploy/dist/providers/fly/commands/setup/setup.js +3 -3
- package/data/packages/deploy/dist/providers/fly/index.js +5 -0
- package/data/packages/deploy/dist/providers/railway/commands/deploy/client.js +2 -1
- package/data/packages/deploy/dist/providers/railway/commands/setup/setup.js +21 -10
- package/data/packages/deploy/dist/providers/railway/index.js +5 -0
- package/data/packages/deploy/dist/providers/railway/jsonOutputSchemas.js +9 -3
- package/data/packages/deploy/dist/providers/railway/railwayService/url.js +8 -1
- package/data/packages/studio/dist/public/assets/Flow-_d98T2dd.js +26 -0
- package/data/packages/studio/dist/public/assets/index-B6X8EdJH.js +21 -0
- package/data/packages/studio/dist/public/assets/index-CXlD_bzV.js +1 -0
- package/data/packages/studio/dist/public/assets/index-IWX3d-Jz.css +1 -0
- package/data/packages/studio/dist/public/index.html +2 -3
- package/package.json +1 -1
- package/wasp-bin +0 -0
- package/data/Cli/starters/basic/postcss.config.js +0 -6
- package/data/Cli/starters/basic/src/shared/components/Portal.tsx +0 -26
- package/data/Cli/starters/basic/tailwind.config.js +0 -30
- package/data/Generator/templates/react-app/README.md +0 -21
- package/data/Generator/templates/react-app/gitignore +0 -23
- package/data/Generator/templates/react-app/netlify.toml +0 -8
- package/data/Generator/templates/react-app/npmrc +0 -1
- package/data/Generator/templates/react-app/package.json +0 -31
- package/data/Generator/templates/react-app/public/manifest.json +0 -15
- package/data/Generator/templates/react-app/src/index.tsx +0 -47
- package/data/Generator/templates/react-app/src/logo.png +0 -0
- package/data/Generator/templates/react-app/src/router.tsx +0 -59
- package/data/Generator/templates/react-app/src/utils.js +0 -3
- package/data/Generator/templates/react-app/src/vite-env.d.ts +0 -1
- package/data/Generator/templates/react-app/tsconfig.app.json +0 -28
- package/data/Generator/templates/react-app/tsconfig.json +0 -11
- package/data/Generator/templates/react-app/tsconfig.vite.json +0 -16
- package/data/Generator/templates/react-app/vite/detectServerImports.ts +0 -53
- package/data/Generator/templates/react-app/vite.config.ts +0 -74
- package/data/Generator/templates/sdk/wasp/dev/index.ts +0 -19
- package/data/packages/studio/dist/public/assets/Flow-b5112d3d.js +0 -26
- package/data/packages/studio/dist/public/assets/index-17ce6ed4.css +0 -1
- package/data/packages/studio/dist/public/assets/index-62a9d21a.js +0 -120
- /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/FullPageWrapper.tsx +0 -0
- /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.module.css +0 -0
- /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.tsx +0 -0
- /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Message.tsx +0 -0
- /package/data/Generator/templates/{react-app/src/test/vitest → sdk/wasp/client/test}/setup.ts +0 -0
|
@@ -7,7 +7,7 @@ Basic starter is a well-rounded template that showcases the most important bits
|
|
|
7
7
|
- **Node.js** (newest LTS version recommended): We recommend install Node through a Node version manager, e.g. `nvm`.
|
|
8
8
|
- **Wasp** (latest version): Install via
|
|
9
9
|
```sh
|
|
10
|
-
|
|
10
|
+
npm i -g @wasp.sh/wasp-cli@latest
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Using the template
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "__waspAppName__",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"workspaces": [
|
|
4
|
+
"workspaces": [
|
|
5
|
+
".wasp/out/*",
|
|
6
|
+
".wasp/out/sdk/wasp"
|
|
7
|
+
],
|
|
5
8
|
"dependencies": {
|
|
6
9
|
"react": "^19.2.1",
|
|
7
10
|
"react-dom": "^19.2.1",
|
|
8
|
-
"react-hook-form": "^7.
|
|
9
|
-
"react-router
|
|
10
|
-
"tailwind-merge": "~
|
|
11
|
-
"
|
|
11
|
+
"react-hook-form": "^7.69.0",
|
|
12
|
+
"react-router": "^7.12.0",
|
|
13
|
+
"tailwind-merge": "~3.4.0",
|
|
14
|
+
"tailwindcss": "^4.1.18"
|
|
12
15
|
},
|
|
13
16
|
"devDependencies": {
|
|
14
17
|
"@eslint/js": "^9.27.0",
|
|
15
18
|
"@types/react": "^19.2.7",
|
|
16
19
|
"@types/react-dom": "^19.2.3",
|
|
20
|
+
"@tailwindcss/vite": "^4.1.18",
|
|
17
21
|
"eslint": "^9.27.0",
|
|
18
22
|
"eslint-config-prettier": "^10.1.5",
|
|
19
23
|
"eslint-plugin-react": "^7.37.5",
|
|
20
24
|
"globals": "^16.1.0",
|
|
21
25
|
"prettier": "^3.5.3",
|
|
22
|
-
"prettier-plugin-tailwindcss": "^0.
|
|
26
|
+
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
23
27
|
"prisma": "5.19.1",
|
|
24
|
-
"tailwindcss": "^3.4.17",
|
|
25
28
|
"typescript": "5.8.2",
|
|
26
29
|
"typescript-eslint": "^8.32.1",
|
|
27
30
|
"vite": "^7.0.6"
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
@
|
|
2
|
-
|
|
3
|
-
@
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--text-tiny: 0.625rem; /* Font size is 10px */
|
|
5
|
+
--text-tiny--line-height: 1rem;
|
|
6
|
+
|
|
7
|
+
/* Created using https://www.tints.dev */
|
|
8
|
+
--color-primary-50: #fffbeb;
|
|
9
|
+
--color-primary-100: #fff7d6;
|
|
10
|
+
--color-primary-200: #ffefad;
|
|
11
|
+
--color-primary-300: #ffe680;
|
|
12
|
+
--color-primary-400: #ffda47;
|
|
13
|
+
--color-primary-500: #ffcc00;
|
|
14
|
+
--color-primary-600: #e6b800;
|
|
15
|
+
--color-primary-700: #cca300;
|
|
16
|
+
--color-primary-800: #a88700;
|
|
17
|
+
--color-primary-900: #7a6200;
|
|
18
|
+
--color-primary-950: #574500;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
body {
|
|
22
|
+
@apply has-[dialog:open]:overflow-hidden;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@utility card {
|
|
26
|
+
@apply rounded-xl border border-neutral-200 bg-white shadow-xs transition-all duration-200;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@utility label {
|
|
30
|
+
@apply text-sm font-medium text-neutral-700;
|
|
13
31
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export function AuthLayout({ children }: { children: ReactNode }) {
|
|
2
4
|
return (
|
|
3
5
|
<div className="flex justify-center">
|
|
4
6
|
{/* Auth UI has margin-top on title, so we lower the top padding */}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Link } from "react-router
|
|
1
|
+
import { Link } from "react-router";
|
|
2
2
|
import { VerifyEmailForm } from "wasp/client/auth";
|
|
3
3
|
import { AuthLayout } from "../AuthLayout";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ export function EmailVerificationPage() {
|
|
|
8
8
|
<VerifyEmailForm />
|
|
9
9
|
<br />
|
|
10
10
|
<span className="text-sm font-medium text-neutral-900">
|
|
11
|
-
|
|
11
|
+
If everything is okay,{" "}
|
|
12
12
|
<Link to="/login" className="font-semibold underline">
|
|
13
13
|
go to login
|
|
14
14
|
</Link>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Link } from "react-router
|
|
1
|
+
import { Link } from "react-router";
|
|
2
2
|
import { LoginForm } from "wasp/client/auth";
|
|
3
3
|
import { AuthLayout } from "../AuthLayout";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ export function LoginPage() {
|
|
|
8
8
|
<LoginForm />
|
|
9
9
|
<br />
|
|
10
10
|
<span className="text-sm font-medium text-neutral-900">
|
|
11
|
-
|
|
11
|
+
Don't have an account yet?{" "}
|
|
12
12
|
<Link to="/signup" className="font-semibold underline">
|
|
13
13
|
Go to signup
|
|
14
14
|
</Link>
|
|
@@ -16,7 +16,7 @@ export function LoginPage() {
|
|
|
16
16
|
</span>
|
|
17
17
|
<br />
|
|
18
18
|
<span className="text-sm font-medium text-neutral-900">
|
|
19
|
-
|
|
19
|
+
Forgot your password?{" "}
|
|
20
20
|
<Link to="/request-password-reset" className="font-semibold underline">
|
|
21
21
|
Reset it
|
|
22
22
|
</Link>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Link } from "react-router
|
|
1
|
+
import { Link } from "react-router";
|
|
2
2
|
import { ResetPasswordForm } from "wasp/client/auth";
|
|
3
3
|
import { AuthLayout } from "../AuthLayout";
|
|
4
4
|
|
|
@@ -8,7 +8,7 @@ export function PasswordResetPage() {
|
|
|
8
8
|
<ResetPasswordForm />
|
|
9
9
|
<br />
|
|
10
10
|
<span className="text-sm font-medium text-neutral-900">
|
|
11
|
-
|
|
11
|
+
If everything is okay,{" "}
|
|
12
12
|
<Link to="/login" className="font-semibold underline">
|
|
13
13
|
go to login
|
|
14
14
|
</Link>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Link } from "react-router
|
|
1
|
+
import { Link } from "react-router";
|
|
2
2
|
import { SignupForm } from "wasp/client/auth";
|
|
3
3
|
import { AuthLayout } from "../AuthLayout";
|
|
4
4
|
|
|
@@ -23,7 +23,7 @@ export function SignupPage() {
|
|
|
23
23
|
/>
|
|
24
24
|
<br />
|
|
25
25
|
<span className="text-sm font-medium text-neutral-900">
|
|
26
|
-
|
|
26
|
+
Already have an account?{" "}
|
|
27
27
|
<Link to="/login" className="font-semibold underline">
|
|
28
28
|
Go to login
|
|
29
29
|
</Link>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { ComponentProps } from "react";
|
|
1
2
|
import { ClassNameValue, twJoin } from "tailwind-merge";
|
|
2
3
|
import { Link } from "wasp/client/router";
|
|
3
4
|
|
|
4
5
|
type ButtonSize = "md" | "sm" | "xs";
|
|
5
6
|
type ButtonVariant = "primary" | "danger" | "ghost";
|
|
6
7
|
|
|
7
|
-
interface ButtonProps extends
|
|
8
|
+
interface ButtonProps extends ComponentProps<"button"> {
|
|
8
9
|
size?: ButtonSize;
|
|
9
10
|
variant?: ButtonVariant;
|
|
10
11
|
}
|
|
@@ -32,7 +33,7 @@ export function Button({
|
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
type ButtonLinkProps =
|
|
36
|
+
type ButtonLinkProps = ComponentProps<typeof Link> & {
|
|
36
37
|
size?: ButtonSize;
|
|
37
38
|
variant?: ButtonVariant;
|
|
38
39
|
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { MouseEvent, ReactNode, useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
2
3
|
import { twJoin } from "tailwind-merge";
|
|
3
4
|
|
|
4
|
-
interface DialogProps
|
|
5
|
+
interface DialogProps {
|
|
5
6
|
open: boolean;
|
|
6
7
|
onClose: () => void;
|
|
7
8
|
closeOnClickOutside?: boolean;
|
|
9
|
+
children?: ReactNode;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export function Dialog({
|
|
@@ -13,9 +15,9 @@ export function Dialog({
|
|
|
13
15
|
children,
|
|
14
16
|
closeOnClickOutside = true,
|
|
15
17
|
}: DialogProps) {
|
|
16
|
-
const dialogRef =
|
|
18
|
+
const dialogRef = useRef<HTMLDialogElement>(null);
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
useEffect(
|
|
19
21
|
function handleShowOrCloseDialog() {
|
|
20
22
|
const dialog = dialogRef.current;
|
|
21
23
|
if (!dialog) return;
|
|
@@ -29,57 +31,37 @@ export function Dialog({
|
|
|
29
31
|
[open],
|
|
30
32
|
);
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (!closeOnClickOutside) return;
|
|
35
|
-
|
|
34
|
+
const handleClick = useCallback(
|
|
35
|
+
(e: MouseEvent) => {
|
|
36
36
|
const dialog = dialogRef.current;
|
|
37
|
-
if (!dialog) return;
|
|
38
|
-
|
|
39
|
-
const handleClick = (e: MouseEvent) => {
|
|
40
|
-
const rect = dialog.getBoundingClientRect();
|
|
41
|
-
const clickedOutside =
|
|
42
|
-
e.clientX < rect.left ||
|
|
43
|
-
e.clientX > rect.right ||
|
|
44
|
-
e.clientY < rect.top ||
|
|
45
|
-
e.clientY > rect.bottom;
|
|
37
|
+
if (!closeOnClickOutside || !dialog) return;
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
const rect = dialog.getBoundingClientRect();
|
|
40
|
+
const clickedOutside =
|
|
41
|
+
e.clientX < rect.left ||
|
|
42
|
+
e.clientX > rect.right ||
|
|
43
|
+
e.clientY < rect.top ||
|
|
44
|
+
e.clientY > rect.bottom;
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
};
|
|
46
|
+
if (clickedOutside) {
|
|
47
|
+
onClose();
|
|
48
|
+
}
|
|
56
49
|
},
|
|
57
50
|
[closeOnClickOutside, onClose],
|
|
58
51
|
);
|
|
59
52
|
|
|
60
|
-
|
|
61
|
-
function handlePreventScroll() {
|
|
62
|
-
if (!open) return;
|
|
63
|
-
|
|
64
|
-
const originalOverflow = document.body.style.overflow;
|
|
65
|
-
document.body.style.overflow = "hidden";
|
|
66
|
-
return () => {
|
|
67
|
-
document.body.style.overflow = originalOverflow;
|
|
68
|
-
};
|
|
69
|
-
},
|
|
70
|
-
[open],
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
return (
|
|
53
|
+
return createPortal(
|
|
74
54
|
<dialog
|
|
75
55
|
ref={dialogRef}
|
|
76
56
|
className={twJoin(
|
|
77
57
|
"max-h top-[20vh] my-0 flex max-h-[55vh]",
|
|
78
|
-
"bg-transparent backdrop:bg-black/50 backdrop:backdrop-blur-
|
|
58
|
+
"bg-transparent backdrop:bg-black/50 backdrop:backdrop-blur-xs",
|
|
79
59
|
)}
|
|
80
60
|
onClose={onClose}
|
|
61
|
+
onClick={handleClick}
|
|
81
62
|
>
|
|
82
63
|
{children}
|
|
83
|
-
</dialog
|
|
64
|
+
</dialog>,
|
|
65
|
+
document.body,
|
|
84
66
|
);
|
|
85
67
|
}
|
|
@@ -7,8 +7,8 @@ export function Header() {
|
|
|
7
7
|
const { data: user } = useAuth();
|
|
8
8
|
|
|
9
9
|
return (
|
|
10
|
-
<header className="sticky top-0 z-10 flex justify-center border-b border-neutral-200 bg-white shadow">
|
|
11
|
-
<div className="flex w-full max-w-
|
|
10
|
+
<header className="sticky top-0 z-10 flex justify-center border-b border-neutral-200 bg-white shadow-sm">
|
|
11
|
+
<div className="flex w-full max-w-(--breakpoint-lg) items-center justify-between p-4 px-12">
|
|
12
12
|
<Link to="/" className="flex items-center gap-2">
|
|
13
13
|
<img src={Logo} alt="Todo App Logo" className="h-10 w-10" />
|
|
14
14
|
<h1 className="text-2xl font-semibold">Todo App</h1>
|
|
@@ -1,36 +1,33 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ComponentProps, useId } from "react";
|
|
2
2
|
import { ControllerFieldState } from "react-hook-form";
|
|
3
3
|
import { twJoin } from "tailwind-merge";
|
|
4
4
|
|
|
5
|
-
interface InputProps
|
|
6
|
-
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "children" | "id"> {
|
|
5
|
+
interface InputProps extends Omit<ComponentProps<"input">, "children" | "id"> {
|
|
7
6
|
label: string;
|
|
8
7
|
fieldState: ControllerFieldState;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{...props}
|
|
26
|
-
ref={ref}
|
|
27
|
-
/>
|
|
28
|
-
{fieldState.error && (
|
|
29
|
-
<span className="text-sm text-red-500">
|
|
30
|
-
{fieldState.error.message}
|
|
31
|
-
</span>
|
|
10
|
+
export function Input({ className, label, fieldState, ...props }: InputProps) {
|
|
11
|
+
const id = useId();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex flex-col gap-1">
|
|
15
|
+
<label htmlFor={id} className="label">
|
|
16
|
+
{label}
|
|
17
|
+
</label>
|
|
18
|
+
|
|
19
|
+
<input
|
|
20
|
+
id={id}
|
|
21
|
+
className={twJoin(
|
|
22
|
+
"w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-neutral-800 shadow-xs focus:border-primary-500 focus:outline-hidden focus:ring-1 focus:ring-primary-500",
|
|
23
|
+
className,
|
|
32
24
|
)}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
{fieldState.error && (
|
|
29
|
+
<span className="text-sm text-red-500">{fieldState.error.message}</span>
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ComponentProps, useId } from "react";
|
|
2
2
|
import { twJoin } from "tailwind-merge";
|
|
3
3
|
|
|
4
|
-
interface ColorRadioButtonProps
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
> {
|
|
4
|
+
interface ColorRadioButtonProps extends Required<
|
|
5
|
+
Pick<
|
|
6
|
+
ComponentProps<"input">,
|
|
7
|
+
"name" | "checked" | "value" | "onChange" | "title"
|
|
8
|
+
>
|
|
9
|
+
> {
|
|
11
10
|
bgColor: string;
|
|
12
11
|
}
|
|
13
12
|
|
|
@@ -17,7 +16,7 @@ export function ColorRadioButton({
|
|
|
17
16
|
title,
|
|
18
17
|
...props
|
|
19
18
|
}: ColorRadioButtonProps) {
|
|
20
|
-
const id =
|
|
19
|
+
const id = useId();
|
|
21
20
|
|
|
22
21
|
return (
|
|
23
22
|
<div className="relative flex items-center">
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useId, useState } from "react";
|
|
2
2
|
import { Button } from "../../shared/components/Button";
|
|
3
3
|
import { Dialog } from "../../shared/components/Dialog";
|
|
4
|
-
import {
|
|
5
|
-
import { CREATE_TAG_FORM_ID, CreateTagForm } from "./CreateTagForm";
|
|
4
|
+
import { CreateTagForm } from "./CreateTagForm";
|
|
6
5
|
|
|
7
6
|
export function CreateTagDialog() {
|
|
8
|
-
const
|
|
7
|
+
const formId = useId();
|
|
8
|
+
const [tagDialogOpen, setTagDialogOpen] = useState(false);
|
|
9
9
|
|
|
10
10
|
return (
|
|
11
11
|
<>
|
|
@@ -19,31 +19,31 @@ export function CreateTagDialog() {
|
|
|
19
19
|
<span>+</span>
|
|
20
20
|
</Button>
|
|
21
21
|
{tagDialogOpen && (
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</
|
|
44
|
-
</
|
|
45
|
-
</
|
|
46
|
-
</
|
|
22
|
+
<Dialog open={tagDialogOpen} onClose={() => setTagDialogOpen(false)}>
|
|
23
|
+
<section className="card relative flex flex-col">
|
|
24
|
+
<header className="px-4 py-6 lg:px-6 lg:py-8">
|
|
25
|
+
<h2 className="text-xl font-semibold">Create a new tag</h2>
|
|
26
|
+
</header>
|
|
27
|
+
<div className="overflow-y-auto p-4 lg:p-6">
|
|
28
|
+
<CreateTagForm
|
|
29
|
+
id={formId}
|
|
30
|
+
onTagCreated={() => setTagDialogOpen(false)}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
<footer className="flex justify-end gap-2 px-4 py-6 lg:px-6 lg:py-8">
|
|
34
|
+
<Button form={formId} type="submit">
|
|
35
|
+
Create
|
|
36
|
+
</Button>
|
|
37
|
+
<Button
|
|
38
|
+
type="button"
|
|
39
|
+
onClick={() => setTagDialogOpen(false)}
|
|
40
|
+
variant="ghost"
|
|
41
|
+
>
|
|
42
|
+
Cancel
|
|
43
|
+
</Button>
|
|
44
|
+
</footer>
|
|
45
|
+
</section>
|
|
46
|
+
</Dialog>
|
|
47
47
|
)}
|
|
48
48
|
</>
|
|
49
49
|
);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FormEvent } from "react";
|
|
1
2
|
import { Controller, SubmitHandler, useForm } from "react-hook-form";
|
|
2
3
|
import { createTag } from "wasp/client/operations";
|
|
3
4
|
import { Input } from "../../shared/components/Input";
|
|
@@ -6,6 +7,7 @@ import { generateBrightColor } from "./colors";
|
|
|
6
7
|
import { TagLabel } from "./TagLabel";
|
|
7
8
|
|
|
8
9
|
interface CreateTagFormProps {
|
|
10
|
+
id: string;
|
|
9
11
|
onTagCreated: () => void;
|
|
10
12
|
}
|
|
11
13
|
|
|
@@ -14,9 +16,7 @@ interface CreateTagFormValues {
|
|
|
14
16
|
color: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
|
-
export
|
|
18
|
-
|
|
19
|
-
export function CreateTagForm({ onTagCreated }: CreateTagFormProps) {
|
|
19
|
+
export function CreateTagForm({ id, onTagCreated }: CreateTagFormProps) {
|
|
20
20
|
const { handleSubmit, setValue, watch, control, reset } =
|
|
21
21
|
useForm<CreateTagFormValues>({
|
|
22
22
|
defaultValues: {
|
|
@@ -40,7 +40,7 @@ export function CreateTagForm({ onTagCreated }: CreateTagFormProps) {
|
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
42
|
<form
|
|
43
|
-
id={
|
|
43
|
+
id={id}
|
|
44
44
|
onSubmit={stopPropagate(handleSubmit(onSubmit))}
|
|
45
45
|
className="flex flex-col gap-6"
|
|
46
46
|
>
|
|
@@ -90,10 +90,8 @@ export function CreateTagForm({ onTagCreated }: CreateTagFormProps) {
|
|
|
90
90
|
*
|
|
91
91
|
* @see https://github.com/react-hook-form/documentation/issues/916
|
|
92
92
|
*/
|
|
93
|
-
function stopPropagate(
|
|
94
|
-
|
|
95
|
-
) {
|
|
96
|
-
return (e: React.FormEvent<HTMLFormElement>) => {
|
|
93
|
+
function stopPropagate(callback: (event: FormEvent<HTMLFormElement>) => void) {
|
|
94
|
+
return (e: FormEvent<HTMLFormElement>) => {
|
|
97
95
|
e.stopPropagation();
|
|
98
96
|
callback(e);
|
|
99
97
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useCallback } from "react";
|
|
2
2
|
import { Controller, SubmitHandler, useForm } from "react-hook-form";
|
|
3
3
|
import { createTask, getTags, useQuery } from "wasp/client/operations";
|
|
4
4
|
import { Tag } from "wasp/entities";
|
|
@@ -34,7 +34,7 @@ export function CreateTaskForm() {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
const toggleTag =
|
|
37
|
+
const toggleTag = useCallback(
|
|
38
38
|
function toggleTag(id: Tag["id"]) {
|
|
39
39
|
const tagIds = getValues("tagIds");
|
|
40
40
|
if (tagIds.includes(id)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ChangeEvent } from "react";
|
|
1
2
|
import { twJoin } from "tailwind-merge";
|
|
2
3
|
import { updateTaskStatus } from "wasp/client/operations";
|
|
3
4
|
import { TagLabel } from "../../tags/components/TagLabel";
|
|
@@ -9,7 +10,7 @@ interface TaskListItemProps {
|
|
|
9
10
|
|
|
10
11
|
export function TaskListItem({ task }: TaskListItemProps) {
|
|
11
12
|
async function setTaskDone(
|
|
12
|
-
event:
|
|
13
|
+
event: ChangeEvent<HTMLInputElement>,
|
|
13
14
|
): Promise<void> {
|
|
14
15
|
try {
|
|
15
16
|
await updateTaskStatus({
|
|
@@ -36,7 +37,7 @@ export function TaskListItem({ task }: TaskListItemProps) {
|
|
|
36
37
|
checked={task.isDone}
|
|
37
38
|
onChange={setTaskDone}
|
|
38
39
|
/>
|
|
39
|
-
<div className="flex min-w-0 flex-col break-
|
|
40
|
+
<div className="flex min-w-0 flex-col wrap-break-word">
|
|
40
41
|
<p>{task.description}</p>
|
|
41
42
|
<span className="text-xs text-neutral-500">
|
|
42
43
|
{task.createdAt.toLocaleDateString()}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "__waspAppName__",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"workspaces": [".wasp/
|
|
4
|
+
"workspaces": [".wasp/out/*", ".wasp/out/sdk/wasp"],
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"react": "^19.2.1",
|
|
7
7
|
"react-dom": "^19.2.1",
|
|
8
|
-
"react-router
|
|
9
|
-
"wasp": "file:.wasp/out/sdk/wasp"
|
|
8
|
+
"react-router": "^7.12.0"
|
|
10
9
|
},
|
|
11
10
|
"devDependencies": {
|
|
12
11
|
"@types/react": "^19.2.7",
|
|
@@ -1,7 +1,3 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// Types for jest-dom are not recognized automatically and Typescript complains
|
|
5
|
-
// about missing types e.g. when using `toBeInTheDocument` and other matchers.
|
|
6
|
-
// Reference: https://github.com/testing-library/jest-dom/issues/546#issuecomment-1889884843
|
|
7
|
-
import "@testing-library/jest-dom";
|
|
2
|
+
/// <reference types="vitest/globals" />
|
|
3
|
+
/// <reference types="@testing-library/jest-dom" />
|
|
Binary file
|