@pylonsync/create-pylon 0.3.346 → 0.3.348
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/package.json +1 -1
- package/templates/crm/README.md +5 -4
- package/templates/crm/app/companies/companies-view.tsx +6 -3
- package/templates/crm/app/companies/page.tsx +3 -8
- package/templates/crm/app/contacts/contacts-view.tsx +6 -3
- package/templates/crm/app/contacts/page.tsx +3 -8
- package/templates/crm/app/deals/[id]/deal-view.tsx +6 -3
- package/templates/crm/app/deals/[id]/page.tsx +3 -13
- package/templates/crm/app/layout.tsx +4 -4
- package/templates/crm/app/page.tsx +2 -19
- package/templates/crm/app/pipeline-view.tsx +5 -4
- package/templates/crm/app/workspace.tsx +12 -10
- package/templates/crm/{app/login → components}/auth-form.tsx +7 -4
- package/templates/crm/components/require-auth.tsx +66 -0
- package/templates/helpdesk/app/customers/customers-view.tsx +6 -3
- package/templates/helpdesk/app/customers/page.tsx +3 -8
- package/templates/helpdesk/app/inbox-view.tsx +5 -4
- package/templates/helpdesk/app/layout.tsx +4 -4
- package/templates/helpdesk/app/page.tsx +3 -19
- package/templates/helpdesk/app/tickets/[id]/page.tsx +3 -13
- package/templates/helpdesk/app/tickets/[id]/ticket-view.tsx +5 -4
- package/templates/helpdesk/app/workspace.tsx +12 -8
- package/templates/{inventory/app/login → helpdesk/components}/auth-form.tsx +7 -4
- package/templates/helpdesk/components/require-auth.tsx +66 -0
- package/templates/inventory/app/layout.tsx +4 -4
- package/templates/inventory/app/movements/movements-view.tsx +6 -3
- package/templates/inventory/app/movements/page.tsx +3 -8
- package/templates/inventory/app/page.tsx +3 -16
- package/templates/inventory/app/products/[id]/page.tsx +3 -13
- package/templates/inventory/app/products/[id]/product-view.tsx +5 -4
- package/templates/inventory/app/products-view.tsx +5 -4
- package/templates/inventory/app/workspace.tsx +12 -8
- package/templates/{helpdesk/app/login → inventory/components}/auth-form.tsx +7 -4
- package/templates/inventory/components/require-auth.tsx +66 -0
- package/templates/invoices/app/clients/clients-view.tsx +6 -3
- package/templates/invoices/app/clients/page.tsx +3 -8
- package/templates/invoices/app/invoices/[id]/invoice-view.tsx +5 -4
- package/templates/invoices/app/invoices/[id]/page.tsx +3 -13
- package/templates/invoices/app/invoices-view.tsx +5 -4
- package/templates/invoices/app/layout.tsx +4 -4
- package/templates/invoices/app/page.tsx +3 -19
- package/templates/invoices/app/workspace.tsx +13 -8
- package/templates/invoices/{app/login → components}/auth-form.tsx +7 -4
- package/templates/invoices/components/require-auth.tsx +66 -0
- package/templates/projects/app/clients/clients-view.tsx +6 -3
- package/templates/projects/app/clients/page.tsx +3 -8
- package/templates/projects/app/layout.tsx +4 -4
- package/templates/projects/app/page.tsx +3 -15
- package/templates/projects/app/projects/[id]/page.tsx +3 -13
- package/templates/projects/app/projects/[id]/project-view.tsx +5 -4
- package/templates/projects/app/projects-view.tsx +5 -4
- package/templates/projects/app/workspace.tsx +12 -8
- package/templates/projects/components/auth-form.tsx +125 -0
- package/templates/projects/components/require-auth.tsx +66 -0
- package/templates/crm/app/login/page.tsx +0 -38
- package/templates/helpdesk/app/login/page.tsx +0 -38
- package/templates/inventory/app/login/page.tsx +0 -38
- package/templates/invoices/app/login/page.tsx +0 -38
- package/templates/projects/app/login/auth-form.tsx +0 -122
- package/templates/projects/app/login/page.tsx +0 -38
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { Metadata, PageProps } from "@pylonsync/react";
|
|
3
|
-
import { AuthForm } from "./auth-form";
|
|
4
|
-
|
|
5
|
-
export const metadata: Metadata = {
|
|
6
|
-
title: "Sign in",
|
|
7
|
-
robots: "noindex",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default function LoginPage({ auth, response }: PageProps) {
|
|
11
|
-
// Already signed in? Nothing to do here.
|
|
12
|
-
if (auth.user_id && !auth.user_id.startsWith("guest_")) {
|
|
13
|
-
response.redirect("/");
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<main className="flex min-h-screen items-center justify-center px-6">
|
|
19
|
-
<div className="w-full max-w-[320px]">
|
|
20
|
-
<div className="mb-7 flex items-center gap-2">
|
|
21
|
-
<span className="flex size-7 items-center justify-center rounded-md bg-primary text-[12px] font-bold text-primary-foreground">
|
|
22
|
-
C
|
|
23
|
-
</span>
|
|
24
|
-
<span className="text-[15px] font-semibold tracking-tight">CRM</span>
|
|
25
|
-
</div>
|
|
26
|
-
<h1 className="text-[19px] font-semibold tracking-tight">
|
|
27
|
-
Sign in to your workspace
|
|
28
|
-
</h1>
|
|
29
|
-
<p className="mt-1 text-[13px] text-muted-foreground">
|
|
30
|
-
Your team shares one pipeline. Anyone with an account sees it.
|
|
31
|
-
</p>
|
|
32
|
-
<div className="mt-6">
|
|
33
|
-
<AuthForm />
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</main>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { Metadata, PageProps } from "@pylonsync/react";
|
|
3
|
-
import { AuthForm } from "./auth-form";
|
|
4
|
-
|
|
5
|
-
export const metadata: Metadata = {
|
|
6
|
-
title: "Sign in",
|
|
7
|
-
robots: "noindex",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default function LoginPage({ auth, response }: PageProps) {
|
|
11
|
-
// Already signed in? Nothing to do here.
|
|
12
|
-
if (auth.user_id && !auth.user_id.startsWith("guest_")) {
|
|
13
|
-
response.redirect("/");
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<main className="flex min-h-screen items-center justify-center px-6">
|
|
19
|
-
<div className="w-full max-w-[320px]">
|
|
20
|
-
<div className="mb-7 flex items-center gap-2">
|
|
21
|
-
<span className="flex size-7 items-center justify-center rounded-md bg-primary text-[12px] font-bold text-primary-foreground">
|
|
22
|
-
C
|
|
23
|
-
</span>
|
|
24
|
-
<span className="text-[15px] font-semibold tracking-tight">CRM</span>
|
|
25
|
-
</div>
|
|
26
|
-
<h1 className="text-[19px] font-semibold tracking-tight">
|
|
27
|
-
Sign in to your workspace
|
|
28
|
-
</h1>
|
|
29
|
-
<p className="mt-1 text-[13px] text-muted-foreground">
|
|
30
|
-
Your team shares one pipeline. Anyone with an account sees it.
|
|
31
|
-
</p>
|
|
32
|
-
<div className="mt-6">
|
|
33
|
-
<AuthForm />
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</main>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { Metadata, PageProps } from "@pylonsync/react";
|
|
3
|
-
import { AuthForm } from "./auth-form";
|
|
4
|
-
|
|
5
|
-
export const metadata: Metadata = {
|
|
6
|
-
title: "Sign in",
|
|
7
|
-
robots: "noindex",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default function LoginPage({ auth, response }: PageProps) {
|
|
11
|
-
// Already signed in? Nothing to do here.
|
|
12
|
-
if (auth.user_id && !auth.user_id.startsWith("guest_")) {
|
|
13
|
-
response.redirect("/");
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<main className="flex min-h-screen items-center justify-center px-6">
|
|
19
|
-
<div className="w-full max-w-[320px]">
|
|
20
|
-
<div className="mb-7 flex items-center gap-2">
|
|
21
|
-
<span className="flex size-7 items-center justify-center rounded-md bg-primary text-[12px] font-bold text-primary-foreground">
|
|
22
|
-
C
|
|
23
|
-
</span>
|
|
24
|
-
<span className="text-[15px] font-semibold tracking-tight">CRM</span>
|
|
25
|
-
</div>
|
|
26
|
-
<h1 className="text-[19px] font-semibold tracking-tight">
|
|
27
|
-
Sign in to your workspace
|
|
28
|
-
</h1>
|
|
29
|
-
<p className="mt-1 text-[13px] text-muted-foreground">
|
|
30
|
-
Your team shares one pipeline. Anyone with an account sees it.
|
|
31
|
-
</p>
|
|
32
|
-
<div className="mt-6">
|
|
33
|
-
<AuthForm />
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</main>
|
|
37
|
-
);
|
|
38
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import React, { useState } from "react";
|
|
4
|
-
import {
|
|
5
|
-
ApiError,
|
|
6
|
-
passwordLogin,
|
|
7
|
-
passwordRegister,
|
|
8
|
-
persistSession,
|
|
9
|
-
} from "@pylonsync/client";
|
|
10
|
-
import { Button } from "@/components/ui/button";
|
|
11
|
-
import { Input } from "@/components/ui/input";
|
|
12
|
-
import { Label } from "@/components/ui/label";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* One form, two modes. Calls the built-in auth API (`/api/auth/password/*`),
|
|
16
|
-
* then `persistSession` writes the token so the sync engine and `callFn`
|
|
17
|
-
* authenticate as this user on the next load.
|
|
18
|
-
*
|
|
19
|
-
* The navigation is a full `assign`, not a client route change, so the SSR
|
|
20
|
-
* runtime re-resolves auth from the HttpOnly cookie and the first paint of the
|
|
21
|
-
* workspace is already signed in.
|
|
22
|
-
*/
|
|
23
|
-
export function AuthForm() {
|
|
24
|
-
const [mode, setMode] = useState<"login" | "signup">("login");
|
|
25
|
-
const [email, setEmail] = useState("");
|
|
26
|
-
const [password, setPassword] = useState("");
|
|
27
|
-
const [error, setError] = useState<string | null>(null);
|
|
28
|
-
const [pending, setPending] = useState(false);
|
|
29
|
-
|
|
30
|
-
async function onSubmit(event: React.FormEvent) {
|
|
31
|
-
event.preventDefault();
|
|
32
|
-
setError(null);
|
|
33
|
-
setPending(true);
|
|
34
|
-
try {
|
|
35
|
-
const session =
|
|
36
|
-
mode === "login"
|
|
37
|
-
? await passwordLogin({ email, password })
|
|
38
|
-
: await passwordRegister({ email, password });
|
|
39
|
-
persistSession(session);
|
|
40
|
-
window.location.assign("/");
|
|
41
|
-
} catch (err) {
|
|
42
|
-
setError(messageFor(err));
|
|
43
|
-
setPending(false);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div className="space-y-5">
|
|
49
|
-
<form onSubmit={onSubmit} className="space-y-3.5">
|
|
50
|
-
<div className="space-y-1.5">
|
|
51
|
-
<Label htmlFor="email">Email</Label>
|
|
52
|
-
<Input
|
|
53
|
-
id="email"
|
|
54
|
-
type="email"
|
|
55
|
-
value={email}
|
|
56
|
-
onChange={(event) => setEmail(event.target.value)}
|
|
57
|
-
required
|
|
58
|
-
autoComplete="email"
|
|
59
|
-
placeholder="you@company.com"
|
|
60
|
-
/>
|
|
61
|
-
</div>
|
|
62
|
-
<div className="space-y-1.5">
|
|
63
|
-
<Label htmlFor="password">Password</Label>
|
|
64
|
-
<Input
|
|
65
|
-
id="password"
|
|
66
|
-
type="password"
|
|
67
|
-
value={password}
|
|
68
|
-
onChange={(event) => setPassword(event.target.value)}
|
|
69
|
-
required
|
|
70
|
-
autoComplete={mode === "login" ? "current-password" : "new-password"}
|
|
71
|
-
placeholder={mode === "login" ? "Your password" : "At least 10 characters"}
|
|
72
|
-
/>
|
|
73
|
-
</div>
|
|
74
|
-
{error ? (
|
|
75
|
-
<p className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-[12px] leading-snug text-destructive">
|
|
76
|
-
{error}
|
|
77
|
-
</p>
|
|
78
|
-
) : null}
|
|
79
|
-
<Button type="submit" disabled={pending} className="w-full">
|
|
80
|
-
{pending ? "…" : mode === "login" ? "Sign in" : "Create account"}
|
|
81
|
-
</Button>
|
|
82
|
-
</form>
|
|
83
|
-
|
|
84
|
-
<p className="text-center text-[12px] text-muted-foreground">
|
|
85
|
-
{mode === "login" ? "First time here?" : "Already have an account?"}{" "}
|
|
86
|
-
<button
|
|
87
|
-
type="button"
|
|
88
|
-
onClick={() => {
|
|
89
|
-
setMode(mode === "login" ? "signup" : "login");
|
|
90
|
-
setError(null);
|
|
91
|
-
}}
|
|
92
|
-
className="font-medium text-foreground underline underline-offset-2"
|
|
93
|
-
>
|
|
94
|
-
{mode === "login" ? "Create an account" : "Sign in"}
|
|
95
|
-
</button>
|
|
96
|
-
</p>
|
|
97
|
-
</div>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// `ApiError` carries a stable `.code`, so branch on the code rather than the
|
|
102
|
-
// message — the copy here is ours, the message is the framework's.
|
|
103
|
-
function messageFor(err: unknown): string {
|
|
104
|
-
if (err instanceof ApiError) {
|
|
105
|
-
switch (err.code) {
|
|
106
|
-
case "INVALID_CREDENTIALS":
|
|
107
|
-
return "Wrong email or password.";
|
|
108
|
-
case "USER_EXISTS":
|
|
109
|
-
return "That email is already registered — sign in instead.";
|
|
110
|
-
case "WEAK_PASSWORD":
|
|
111
|
-
return "Pick a longer password — at least 10 characters.";
|
|
112
|
-
case "PWNED_PASSWORD":
|
|
113
|
-
return "That password has appeared in a known breach. Choose another.";
|
|
114
|
-
case "RATE_LIMITED":
|
|
115
|
-
return "Too many attempts — try again in a minute.";
|
|
116
|
-
default:
|
|
117
|
-
return err.message;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (err instanceof Error) return err.message;
|
|
121
|
-
return "Something went wrong. Try again.";
|
|
122
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import type { Metadata, PageProps } from "@pylonsync/react";
|
|
3
|
-
import { AuthForm } from "./auth-form";
|
|
4
|
-
|
|
5
|
-
export const metadata: Metadata = {
|
|
6
|
-
title: "Sign in",
|
|
7
|
-
robots: "noindex",
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export default function LoginPage({ auth, response }: PageProps) {
|
|
11
|
-
// Already signed in? Nothing to do here.
|
|
12
|
-
if (auth.user_id && !auth.user_id.startsWith("guest_")) {
|
|
13
|
-
response.redirect("/");
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<main className="flex min-h-screen items-center justify-center px-6">
|
|
19
|
-
<div className="w-full max-w-[320px]">
|
|
20
|
-
<div className="mb-7 flex items-center gap-2">
|
|
21
|
-
<span className="flex size-7 items-center justify-center rounded-md bg-primary text-[12px] font-bold text-primary-foreground">
|
|
22
|
-
C
|
|
23
|
-
</span>
|
|
24
|
-
<span className="text-[15px] font-semibold tracking-tight">CRM</span>
|
|
25
|
-
</div>
|
|
26
|
-
<h1 className="text-[19px] font-semibold tracking-tight">
|
|
27
|
-
Sign in to your workspace
|
|
28
|
-
</h1>
|
|
29
|
-
<p className="mt-1 text-[13px] text-muted-foreground">
|
|
30
|
-
Your team shares one pipeline. Anyone with an account sees it.
|
|
31
|
-
</p>
|
|
32
|
-
<div className="mt-6">
|
|
33
|
-
<AuthForm />
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</main>
|
|
37
|
-
);
|
|
38
|
-
}
|