mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 20:51:17 +00:00
see comments
This commit is contained in:
parent
5edd697056
commit
b252264211
4 changed files with 34 additions and 9 deletions
|
|
@ -14,4 +14,8 @@ begin
|
||||||
);
|
);
|
||||||
return new;
|
return new;
|
||||||
end;
|
end;
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
create trigger on_auth_user_created
|
||||||
|
after insert on auth.users
|
||||||
|
for each row execute function public.handle_new_user();
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use server";
|
"use server";
|
||||||
import { createAdminClient } from "@/lib/supabase/admin";
|
import { createAdminClient } from "@/lib/supabase/admin";
|
||||||
|
import { verifySession } from "@/lib/supabase/dal";
|
||||||
import { generatePassword } from "@/lib/generate-password";
|
import { generatePassword } from "@/lib/generate-password";
|
||||||
import * as z from "zod";
|
import * as z from "zod";
|
||||||
|
|
||||||
|
|
@ -20,6 +21,11 @@ export async function createClient(
|
||||||
formData: FormData
|
formData: FormData
|
||||||
): Promise<CreateClientState> {
|
): Promise<CreateClientState> {
|
||||||
|
|
||||||
|
const session = await verifySession();
|
||||||
|
if (!session.isAdmin) {
|
||||||
|
return { error: "Keine Berechtigung." }
|
||||||
|
}
|
||||||
|
|
||||||
const validatedFields = CreateClientSchema.safeParse({
|
const validatedFields = CreateClientSchema.safeParse({
|
||||||
name: formData.get("name"),
|
name: formData.get("name"),
|
||||||
email: formData.get("email")
|
email: formData.get("email")
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export async function getClients() {
|
||||||
.eq("role", "client")
|
.eq("role", "client")
|
||||||
.order("name")
|
.order("name")
|
||||||
|
|
||||||
if (error) return []
|
if (error) throw new Error(`Failed to load clients: ${error.message}`)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,31 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { CheckIcon, CopyIcon } from "lucide-react";
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Field, FieldLabel } from "@/components/ui/field";
|
import { Field, FieldLabel } from "@/components/ui/field";
|
||||||
|
|
||||||
export function CopyField({ label, value }: { label: string; value: string }) {
|
export function CopyField({ label, value }: { label: string; value: string }) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [status, setStatus] = useState<"idle" | "copied" | "error">("idle");
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
async function handleCopy() {
|
async function handleCopy() {
|
||||||
await navigator.clipboard.writeText(value);
|
try {
|
||||||
setCopied(true);
|
await navigator.clipboard.writeText(value);
|
||||||
setTimeout(() => setCopied(false), 1500);
|
setStatus("copied");
|
||||||
|
} catch {
|
||||||
|
inputRef.current?.select();
|
||||||
|
setStatus("error");
|
||||||
|
}
|
||||||
|
setTimeout(() => setStatus("idle"), 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field>
|
<Field>
|
||||||
<FieldLabel>{label}</FieldLabel>
|
<FieldLabel>{label}</FieldLabel>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Input value={value} readOnly className="focus-visible:ring-0" />
|
<Input ref={inputRef} value={value} readOnly className="focus-visible:ring-0" />
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|
@ -28,9 +34,18 @@ export function CopyField({ label, value }: { label: string; value: string }) {
|
||||||
onClick={handleCopy}
|
onClick={handleCopy}
|
||||||
aria-label={`${label} kopieren`}
|
aria-label={`${label} kopieren`}
|
||||||
>
|
>
|
||||||
{copied ? <CheckIcon className="text-signal-success" /> : <CopyIcon />}
|
{status === "copied" ? (
|
||||||
|
<CheckIcon className="text-signal-success" />
|
||||||
|
) : (
|
||||||
|
<CopyIcon />
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
{status === "error" && (
|
||||||
|
<p className="text-small text-destructive">
|
||||||
|
Kopieren fehlgeschlagen — Text wurde markiert, bitte manuell kopieren.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</Field>
|
</Field>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue