@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.
Files changed (118) hide show
  1. package/data/Cli/starters/basic/README.md +1 -1
  2. package/data/Cli/starters/basic/package.json +10 -7
  3. package/data/Cli/starters/basic/src/App.css +30 -12
  4. package/data/Cli/starters/basic/src/App.tsx +1 -1
  5. package/data/Cli/starters/basic/src/auth/AuthLayout.tsx +3 -1
  6. package/data/Cli/starters/basic/src/auth/email/EmailVerificationPage.tsx +2 -2
  7. package/data/Cli/starters/basic/src/auth/email/LoginPage.tsx +3 -3
  8. package/data/Cli/starters/basic/src/auth/email/PasswordResetPage.tsx +2 -2
  9. package/data/Cli/starters/basic/src/auth/email/SignupPage.tsx +2 -2
  10. package/data/Cli/starters/basic/src/shared/components/Button.tsx +3 -2
  11. package/data/Cli/starters/basic/src/shared/components/Dialog.tsx +23 -41
  12. package/data/Cli/starters/basic/src/shared/components/Header.tsx +2 -2
  13. package/data/Cli/starters/basic/src/shared/components/Input.tsx +25 -28
  14. package/data/Cli/starters/basic/src/tags/components/ColorRadioButton.tsx +8 -9
  15. package/data/Cli/starters/basic/src/tags/components/CreateTagDialog.tsx +29 -29
  16. package/data/Cli/starters/basic/src/tags/components/CreateTagForm.tsx +6 -8
  17. package/data/Cli/starters/basic/src/tasks/components/CreateTaskForm.tsx +2 -2
  18. package/data/Cli/starters/basic/src/tasks/components/TaskListItem.tsx +3 -2
  19. package/data/Cli/starters/basic/vite.config.ts +3 -0
  20. package/data/Cli/starters/minimal/package.json +2 -3
  21. package/data/Cli/starters/minimal/vite.config.ts +2 -0
  22. package/data/Cli/starters/skeleton/src/vite-env.d.ts +2 -6
  23. package/data/Generator/libs/auth/wasp.sh-lib-auth-0.21.0.tgz +0 -0
  24. package/data/Generator/templates/Dockerfile +13 -12
  25. package/data/Generator/templates/sdk/wasp/auth/forms/Auth.tsx +2 -11
  26. package/data/Generator/templates/sdk/wasp/auth/forms/internal/common/LoginSignupForm.tsx +10 -4
  27. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ForgotPasswordForm.tsx +4 -3
  28. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/ResetPasswordForm.tsx +4 -4
  29. package/data/Generator/templates/sdk/wasp/auth/forms/internal/email/VerifyEmailForm.tsx +3 -4
  30. package/data/Generator/templates/sdk/wasp/auth/forms/internal/social/SocialIcons.tsx +11 -0
  31. package/data/Generator/templates/sdk/wasp/auth/forms/types.ts +0 -6
  32. package/data/Generator/templates/sdk/wasp/auth/jwt.ts +9 -16
  33. package/data/Generator/templates/sdk/wasp/auth/password.ts +1 -36
  34. package/data/Generator/templates/sdk/wasp/auth/utils.ts +2 -0
  35. package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/DefaultRootErrorBoundary.tsx +1 -1
  36. package/data/Generator/templates/sdk/wasp/client/app/components/WaspApp.tsx +45 -0
  37. package/data/Generator/templates/sdk/wasp/client/app/index.tsx +26 -0
  38. package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/OAuthCallback.tsx +9 -8
  39. package/data/Generator/templates/{react-app/src/auth → sdk/wasp/client/app}/pages/createAuthRequiredPage.jsx +7 -9
  40. package/data/Generator/templates/sdk/wasp/client/app/router/router.tsx +47 -0
  41. package/data/Generator/templates/sdk/wasp/client/auth/index.ts +3 -0
  42. package/data/Generator/templates/sdk/wasp/client/auth/microsoft.ts +2 -0
  43. package/data/Generator/templates/sdk/wasp/client/auth/ui.ts +3 -0
  44. package/data/Generator/templates/sdk/wasp/client/router/Link.tsx +2 -2
  45. package/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx +10 -12
  46. package/data/Generator/templates/sdk/wasp/client/vite/index.ts +1 -0
  47. package/data/Generator/templates/sdk/wasp/client/vite/plugins/detectServerImports.ts +50 -0
  48. package/data/Generator/templates/sdk/wasp/client/vite/plugins/envFile.ts +112 -0
  49. package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/build.ts +38 -0
  50. package/data/Generator/templates/sdk/wasp/client/vite/plugins/html/dev.ts +35 -0
  51. package/data/Generator/templates/sdk/wasp/client/vite/plugins/typescriptCheck.ts +32 -0
  52. package/data/Generator/templates/{react-app/vite → sdk/wasp/client/vite/plugins}/validateEnv.ts +17 -7
  53. package/data/Generator/templates/sdk/wasp/client/vite/plugins/virtualModules.ts +29 -0
  54. package/data/Generator/templates/sdk/wasp/client/vite/plugins/wasp.ts +36 -0
  55. package/data/Generator/templates/sdk/wasp/client/vite/plugins/waspConfig.ts +56 -0
  56. package/data/Generator/templates/{react-app → sdk/wasp/client/vite/virtual-files/files}/index.html +1 -2
  57. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/index.tsx +34 -0
  58. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/files/routes.tsx +17 -0
  59. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/index.ts +20 -0
  60. package/data/Generator/templates/sdk/wasp/client/vite/virtual-files/resolver.ts +28 -0
  61. package/data/Generator/templates/sdk/wasp/package.json +6 -4
  62. package/data/Generator/templates/sdk/wasp/scripts/copy-assets.js +67 -7
  63. package/data/Generator/templates/sdk/wasp/server/auth/hooks.ts +3 -0
  64. package/data/Generator/templates/sdk/wasp/server/auth/oauth/index.ts +4 -0
  65. package/data/Generator/templates/sdk/wasp/server/auth/oauth/providers/microsoft.ts +23 -0
  66. package/data/Generator/templates/sdk/wasp/server/auth/user.ts +6 -0
  67. package/data/Generator/templates/sdk/wasp/server/env.ts +11 -0
  68. package/data/Generator/templates/sdk/wasp/tsconfig.json +5 -1
  69. package/data/Generator/templates/server/src/auth/providers/config/microsoft.ts +65 -0
  70. package/data/Generator/templates/server/src/auth/providers/oauth/cookies.ts +1 -1
  71. package/data/packages/deploy/dist/common/clientApp.js +4 -11
  72. package/data/packages/deploy/dist/common/terminal.js +7 -0
  73. package/data/packages/deploy/dist/common/waspProject.js +11 -7
  74. package/data/packages/deploy/dist/providers/fly/CommonOps.js +3 -3
  75. package/data/packages/deploy/dist/providers/fly/commands/cmd/cmd.js +1 -1
  76. package/data/packages/deploy/dist/providers/fly/commands/deploy/deploy.js +6 -5
  77. package/data/packages/deploy/dist/providers/fly/commands/setup/setup.js +3 -3
  78. package/data/packages/deploy/dist/providers/fly/index.js +5 -0
  79. package/data/packages/deploy/dist/providers/railway/commands/deploy/client.js +2 -1
  80. package/data/packages/deploy/dist/providers/railway/commands/setup/setup.js +21 -10
  81. package/data/packages/deploy/dist/providers/railway/index.js +5 -0
  82. package/data/packages/deploy/dist/providers/railway/jsonOutputSchemas.js +9 -3
  83. package/data/packages/deploy/dist/providers/railway/railwayService/url.js +8 -1
  84. package/data/packages/studio/dist/public/assets/Flow-_d98T2dd.js +26 -0
  85. package/data/packages/studio/dist/public/assets/index-B6X8EdJH.js +21 -0
  86. package/data/packages/studio/dist/public/assets/index-CXlD_bzV.js +1 -0
  87. package/data/packages/studio/dist/public/assets/index-IWX3d-Jz.css +1 -0
  88. package/data/packages/studio/dist/public/index.html +2 -3
  89. package/package.json +1 -1
  90. package/wasp-bin +0 -0
  91. package/data/Cli/starters/basic/postcss.config.js +0 -6
  92. package/data/Cli/starters/basic/src/shared/components/Portal.tsx +0 -26
  93. package/data/Cli/starters/basic/tailwind.config.js +0 -30
  94. package/data/Generator/templates/react-app/README.md +0 -21
  95. package/data/Generator/templates/react-app/gitignore +0 -23
  96. package/data/Generator/templates/react-app/netlify.toml +0 -8
  97. package/data/Generator/templates/react-app/npmrc +0 -1
  98. package/data/Generator/templates/react-app/package.json +0 -31
  99. package/data/Generator/templates/react-app/public/manifest.json +0 -15
  100. package/data/Generator/templates/react-app/src/index.tsx +0 -47
  101. package/data/Generator/templates/react-app/src/logo.png +0 -0
  102. package/data/Generator/templates/react-app/src/router.tsx +0 -59
  103. package/data/Generator/templates/react-app/src/utils.js +0 -3
  104. package/data/Generator/templates/react-app/src/vite-env.d.ts +0 -1
  105. package/data/Generator/templates/react-app/tsconfig.app.json +0 -28
  106. package/data/Generator/templates/react-app/tsconfig.json +0 -11
  107. package/data/Generator/templates/react-app/tsconfig.vite.json +0 -16
  108. package/data/Generator/templates/react-app/vite/detectServerImports.ts +0 -53
  109. package/data/Generator/templates/react-app/vite.config.ts +0 -74
  110. package/data/Generator/templates/sdk/wasp/dev/index.ts +0 -19
  111. package/data/packages/studio/dist/public/assets/Flow-b5112d3d.js +0 -26
  112. package/data/packages/studio/dist/public/assets/index-17ce6ed4.css +0 -1
  113. package/data/packages/studio/dist/public/assets/index-62a9d21a.js +0 -120
  114. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/FullPageWrapper.tsx +0 -0
  115. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.module.css +0 -0
  116. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Loader.tsx +0 -0
  117. /package/data/Generator/templates/{react-app/src → sdk/wasp/client/app}/components/Message.tsx +0 -0
  118. /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
- curl -sSL https://get.wasp.sh/installer.sh | sh
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": [".wasp/build/*", ".wasp/out/*"],
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.57.0",
9
- "react-router-dom": "^6.26.2",
10
- "tailwind-merge": "~2.6.0",
11
- "wasp": "file:.wasp/out/sdk/wasp"
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.6.11",
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
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- @layer components {
6
- .card {
7
- @apply rounded-xl border border-neutral-200 bg-white shadow-sm transition-all duration-200;
8
- }
9
-
10
- .label {
11
- @apply text-sm font-medium text-neutral-700;
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,4 @@
1
- import { Outlet } from "react-router-dom";
1
+ import { Outlet } from "react-router";
2
2
  import "./App.css";
3
3
  import { Header } from "./shared/components/Header";
4
4
 
@@ -1,4 +1,6 @@
1
- export function AuthLayout({ children }: React.PropsWithChildren) {
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-dom";
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
- {"If everything is okay, "}
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-dom";
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
- {"Don't have an account yet? "}
11
+ Don&apos;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
- {"Forgot your password? "}
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-dom";
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
- {"If everything is okay, "}
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-dom";
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
- {"Already have an account? "}
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 React.ButtonHTMLAttributes<HTMLButtonElement> {
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 = React.ComponentProps<typeof Link> & {
36
+ type ButtonLinkProps = ComponentProps<typeof Link> & {
36
37
  size?: ButtonSize;
37
38
  variant?: ButtonVariant;
38
39
  };
@@ -1,10 +1,12 @@
1
- import React from "react";
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 extends React.PropsWithChildren {
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 = React.useRef<HTMLDialogElement>(null);
18
+ const dialogRef = useRef<HTMLDialogElement>(null);
17
19
 
18
- React.useEffect(
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
- React.useEffect(
33
- function handleCloseOnClickOutside() {
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
- if (clickedOutside) {
48
- onClose();
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
- dialog.addEventListener("click", handleClick);
53
- return () => {
54
- dialog.removeEventListener("click", handleClick);
55
- };
46
+ if (clickedOutside) {
47
+ onClose();
48
+ }
56
49
  },
57
50
  [closeOnClickOutside, onClose],
58
51
  );
59
52
 
60
- React.useEffect(
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-sm",
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-screen-lg items-center justify-between p-4 px-12">
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 React from "react";
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 const Input = React.forwardRef<HTMLInputElement, InputProps>(
12
- function Input({ className, label, fieldState, ...props }, ref) {
13
- const id = React.useId();
14
- return (
15
- <div className="flex flex-col gap-1">
16
- <label htmlFor={id} className="label">
17
- {label}
18
- </label>
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-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500",
23
- className,
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
- </div>
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 React from "react";
1
+ import { ComponentProps, useId } from "react";
2
2
  import { twJoin } from "tailwind-merge";
3
3
 
4
- interface ColorRadioButtonProps
5
- extends Required<
6
- Pick<
7
- React.InputHTMLAttributes<HTMLInputElement>,
8
- "name" | "checked" | "value" | "onChange" | "title"
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 = React.useId();
19
+ const id = useId();
21
20
 
22
21
  return (
23
22
  <div className="relative flex items-center">
@@ -1,11 +1,11 @@
1
- import React from "react";
1
+ import { useId, useState } from "react";
2
2
  import { Button } from "../../shared/components/Button";
3
3
  import { Dialog } from "../../shared/components/Dialog";
4
- import { Portal } from "../../shared/components/Portal";
5
- import { CREATE_TAG_FORM_ID, CreateTagForm } from "./CreateTagForm";
4
+ import { CreateTagForm } from "./CreateTagForm";
6
5
 
7
6
  export function CreateTagDialog() {
8
- const [tagDialogOpen, setTagDialogOpen] = React.useState(false);
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
- <Portal>
23
- <Dialog open={tagDialogOpen} onClose={() => setTagDialogOpen(false)}>
24
- <section className="card relative flex flex-col">
25
- <header className="px-4 py-6 lg:px-6 lg:py-8">
26
- <h2 className="text-xl font-semibold">Create a new tag</h2>
27
- </header>
28
- <div className="overflow-y-auto p-4 lg:p-6">
29
- <CreateTagForm onTagCreated={() => setTagDialogOpen(false)} />
30
- </div>
31
- <footer className="flex justify-end gap-2 px-4 py-6 lg:px-6 lg:py-8">
32
- <Button form={CREATE_TAG_FORM_ID} type="submit">
33
- Create
34
- </Button>
35
- <Button
36
- form={CREATE_TAG_FORM_ID}
37
- type="button"
38
- onClick={() => setTagDialogOpen(false)}
39
- variant="ghost"
40
- >
41
- Cancel
42
- </Button>
43
- </footer>
44
- </section>
45
- </Dialog>
46
- </Portal>
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 const CREATE_TAG_FORM_ID = "create-tag";
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={CREATE_TAG_FORM_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
- callback: (event: React.FormEvent<HTMLFormElement>) => void,
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 React from "react";
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 = React.useCallback(
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: React.ChangeEvent<HTMLInputElement>,
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-words">
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,6 +1,9 @@
1
+ import tailwindcss from '@tailwindcss/vite'
1
2
  import { defineConfig } from 'vite'
3
+ import { wasp } from 'wasp/client/vite'
2
4
 
3
5
  export default defineConfig({
6
+ plugins: [wasp(), tailwindcss()],
4
7
  server: {
5
8
  open: true,
6
9
  },
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "__waspAppName__",
3
3
  "type": "module",
4
- "workspaces": [".wasp/build/*", ".wasp/out/*"],
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-dom": "^6.26.2",
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,6 +1,8 @@
1
1
  import { defineConfig } from "vite";
2
+ import { wasp } from "wasp/client/vite";
2
3
 
3
4
  export default defineConfig({
5
+ plugins: [wasp()],
4
6
  server: {
5
7
  open: true,
6
8
  },
@@ -1,7 +1,3 @@
1
1
  /// <reference types="vite/client" />
2
-
3
- // This is needed to properly support Vitest testing with jest-dom matchers.
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" />