livrare website cu erp si crm
This commit is contained in:
parent
28773e3a72
commit
5c7bf7c295
257 changed files with 31929 additions and 0 deletions
202
website/src/app/(dashboard)/dashboard/profile/page.tsx
Normal file
202
website/src/app/(dashboard)/dashboard/profile/page.tsx
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { updateCustomer } from "@/lib/api";
|
||||
|
||||
export default function ProfilePage() {
|
||||
const { data: session } = useSession();
|
||||
const userName = session?.user?.name || "";
|
||||
const userEmail = session?.user?.email || "";
|
||||
const [customer, setCustomer] = useState<Record<string, unknown> | null>(null);
|
||||
const [customerName, setCustomerName] = useState("");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saved, setSaved] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/customer/me")
|
||||
.then((res) => (res.ok ? res.json() : null))
|
||||
.then((res) => {
|
||||
if (res?.data) {
|
||||
setCustomer(res.data);
|
||||
setCustomerName(String(res.data.name || ""));
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
async function handleSave(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
setSaving(true);
|
||||
setSaved(false);
|
||||
const form = new FormData(e.currentTarget);
|
||||
|
||||
try {
|
||||
await updateCustomer(customerName, {
|
||||
customer_name: form.get("company") as string,
|
||||
tax_id: form.get("tax_id") as string,
|
||||
});
|
||||
setSaved(true);
|
||||
} catch {
|
||||
alert("Eroare la salvare");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Profil</h1>
|
||||
<p className="mt-4 text-gray-500">Se incarca...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Profil</h1>
|
||||
<p className="mt-1 text-sm text-gray-500">Gestioneaza datele tale personale.</p>
|
||||
|
||||
<form onSubmit={handleSave} className="mt-6 max-w-lg space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Nume</label>
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={userName}
|
||||
disabled
|
||||
className="mt-1 block w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-2.5 text-sm text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
defaultValue={userEmail}
|
||||
disabled
|
||||
className="mt-1 block w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-2.5 text-sm text-gray-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">Companie</label>
|
||||
<input
|
||||
name="company"
|
||||
type="text"
|
||||
defaultValue={String(customer?.customer_name || "")}
|
||||
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700">CUI</label>
|
||||
<input
|
||||
name="tax_id"
|
||||
type="text"
|
||||
defaultValue={String(customer?.tax_id || "")}
|
||||
className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
type="submit"
|
||||
disabled={saving}
|
||||
className="rounded-lg bg-teal-600 px-6 py-2.5 text-sm font-semibold text-white hover:bg-teal-700 disabled:opacity-50"
|
||||
>
|
||||
{saving ? "Se salveaza..." : "Salveaza"}
|
||||
</button>
|
||||
{saved && <span className="text-sm text-green-600">Salvat!</span>}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* GDPR section */}
|
||||
<GdprActions />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GdprActions() {
|
||||
const [busy, setBusy] = useState<string | null>(null);
|
||||
const [msg, setMsg] = useState<string | null>(null);
|
||||
|
||||
function exportData() {
|
||||
setBusy("export");
|
||||
setMsg(null);
|
||||
// Open download in new tab — server returns Content-Disposition: attachment
|
||||
window.open("/api/gdpr/export", "_blank");
|
||||
setTimeout(() => {
|
||||
setBusy(null);
|
||||
setMsg("Datele tale au fost descarcate ca fisier JSON.");
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
async function requestDelete() {
|
||||
const ok = confirm(
|
||||
"Esti SIGUR ca vrei sa stergi contul?\n\n" +
|
||||
"- Analizele si rapoartele tale vor fi anonimizate\n" +
|
||||
"- Facturile fiscale raman in arhiva 10 ani (cerinta legala RO)\n" +
|
||||
"- Datele personale vor fi sterse in 30 zile\n" +
|
||||
"- Actiunea este IREVERSIBILA dupa 30 zile\n\n" +
|
||||
"Continui?"
|
||||
);
|
||||
if (!ok) return;
|
||||
setBusy("delete");
|
||||
setMsg(null);
|
||||
try {
|
||||
const res = await fetch("/api/gdpr/delete", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ confirm: true, reason: "Solicitare self-service din dashboard" }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
setMsg(data.message || "Cererea a fost inregistrata.");
|
||||
} else {
|
||||
setMsg(`Eroare: ${data.error}`);
|
||||
}
|
||||
} catch (e) {
|
||||
setMsg(`Eroare: ${e instanceof Error ? e.message : "necunoscuta"}`);
|
||||
} finally {
|
||||
setBusy(null);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-10 border-t pt-8">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Protectia datelor (GDPR)</h2>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
Exercita-ti drepturile conform Regulamentului (UE) 2016/679 (GDPR).
|
||||
</p>
|
||||
|
||||
<div className="mt-4 grid gap-3 sm:grid-cols-2">
|
||||
<button
|
||||
disabled={!!busy}
|
||||
onClick={exportData}
|
||||
className="rounded-lg border p-4 text-left text-sm hover:bg-gray-50 disabled:opacity-50"
|
||||
>
|
||||
<div className="font-semibold text-gray-900">Exporta datele mele</div>
|
||||
<div className="mt-1 text-xs text-gray-500">
|
||||
Art. 20 GDPR — portabilitate. Descarca toate datele tale (profil, facturi, plati, analize) in format JSON.
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
disabled={!!busy}
|
||||
onClick={requestDelete}
|
||||
className="rounded-lg border border-red-200 p-4 text-left text-sm hover:bg-red-50 disabled:opacity-50"
|
||||
>
|
||||
<div className="font-semibold text-red-700">Solicita stergerea contului</div>
|
||||
<div className="mt-1 text-xs text-red-600">
|
||||
Art. 17 GDPR — dreptul la stergere. Cererea va fi procesata in 30 zile.
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{msg && (
|
||||
<p className={`mt-4 text-sm ${msg.startsWith("Eroare") ? "text-red-600" : "text-green-700"}`}>
|
||||
{msg}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue