init: lab project bootstrapped from isletmem-kovakcrm
- CRM domain modules removed (customers, services, software, calendar, tasks, invoices, leads, finance, etc.)
- DLS branding: package name=lab, logo wordmark, sidebar nav, header CTA
- Tenant layer extended with kind dimension (lab|clinic) + requireTenantKind helper
- Schema rewritten for DLS domain: jobs, job_files, job_status_history, prosthetics, connections, finance_entries, notifications
- Onboarding form: clinic/lab account-type selection + auto-generated memberNumber
- Placeholder routes for jobs/{inbound,outbound,new}, products, finance, connections
- PDF spec + spec.md under belgeler/
- db: lab database + 13 collections + indexes + storage bucket (job-files) provisioned via Appwrite MCP
Ref: belgeler/dls-ui-tasarim.pdf
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { AppwriteException } from "node-appwrite";
|
||||
|
||||
import { logAudit } from "./audit";
|
||||
import { createSessionClient } from "./server";
|
||||
import { getActiveTenantId } from "./tenant";
|
||||
import type { ProfileState } from "./profile-types";
|
||||
|
||||
function appwriteError(e: unknown): string {
|
||||
if (e instanceof AppwriteException) {
|
||||
if (e.type === "user_invalid_credentials") return "Şifre hatalı.";
|
||||
if (e.type === "user_password_mismatch") return "Şifreler eşleşmiyor.";
|
||||
if (e.type === "user_email_already_exists")
|
||||
return "Bu email zaten başka bir hesapta kullanımda.";
|
||||
if (e.type === "user_password_recently_used")
|
||||
return "Bu şifreyi yakın zamanda kullandınız, başka bir şifre seçin.";
|
||||
if (e.type === "general_rate_limit_exceeded")
|
||||
return "Çok fazla deneme. Birkaç dakika sonra tekrar deneyin.";
|
||||
return e.message || "Beklenmeyen hata.";
|
||||
}
|
||||
return "Bağlantı hatası. Tekrar deneyin.";
|
||||
}
|
||||
|
||||
async function audit(action: "update", entityType: string, changes: Record<string, unknown>) {
|
||||
try {
|
||||
const session = await createSessionClient();
|
||||
const user = await session.account.get();
|
||||
const tenantId = (await getActiveTenantId()) ?? "global";
|
||||
await logAudit({
|
||||
tenantId,
|
||||
userId: user.$id,
|
||||
action,
|
||||
entityType,
|
||||
entityId: user.$id,
|
||||
changes,
|
||||
});
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateNameAction(
|
||||
_prev: ProfileState,
|
||||
formData: FormData,
|
||||
): Promise<ProfileState> {
|
||||
const name = String(formData.get("name") ?? "").trim();
|
||||
if (!name) {
|
||||
return { ok: false, error: "İsim boş olamaz.", fieldErrors: { name: "Zorunlu" } };
|
||||
}
|
||||
if (name.length > 128) {
|
||||
return { ok: false, error: "İsim çok uzun." };
|
||||
}
|
||||
|
||||
try {
|
||||
const { account } = await createSessionClient();
|
||||
await account.updateName(name);
|
||||
await audit("update", "user_name", { name });
|
||||
} catch (e) {
|
||||
return { ok: false, error: appwriteError(e) };
|
||||
}
|
||||
|
||||
revalidatePath("/", "layout");
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function updateEmailAction(
|
||||
_prev: ProfileState,
|
||||
formData: FormData,
|
||||
): Promise<ProfileState> {
|
||||
const email = String(formData.get("email") ?? "").trim().toLowerCase();
|
||||
const password = String(formData.get("password") ?? "");
|
||||
|
||||
if (!email || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "Geçerli bir email girin.",
|
||||
fieldErrors: { email: "Geçersiz" },
|
||||
};
|
||||
}
|
||||
if (!password) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "Doğrulama için şifrenizi girin.",
|
||||
fieldErrors: { password: "Zorunlu" },
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const { account } = await createSessionClient();
|
||||
await account.updateEmail(email, password);
|
||||
await audit("update", "user_email", { email });
|
||||
} catch (e) {
|
||||
return { ok: false, error: appwriteError(e) };
|
||||
}
|
||||
|
||||
revalidatePath("/", "layout");
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
export async function updatePasswordAction(
|
||||
_prev: ProfileState,
|
||||
formData: FormData,
|
||||
): Promise<ProfileState> {
|
||||
const oldPassword = String(formData.get("oldPassword") ?? "");
|
||||
const newPassword = String(formData.get("newPassword") ?? "");
|
||||
const confirmPassword = String(formData.get("confirmPassword") ?? "");
|
||||
|
||||
if (!oldPassword) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "Mevcut şifrenizi girin.",
|
||||
fieldErrors: { oldPassword: "Zorunlu" },
|
||||
};
|
||||
}
|
||||
if (newPassword.length < 8) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "Yeni şifre en az 8 karakter olmalı.",
|
||||
fieldErrors: { newPassword: "En az 8 karakter" },
|
||||
};
|
||||
}
|
||||
if (newPassword !== confirmPassword) {
|
||||
return {
|
||||
ok: false,
|
||||
error: "Şifreler eşleşmiyor.",
|
||||
fieldErrors: { confirmPassword: "Eşleşmiyor" },
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const { account } = await createSessionClient();
|
||||
await account.updatePassword(newPassword, oldPassword);
|
||||
await audit("update", "user_password", { changed: true });
|
||||
} catch (e) {
|
||||
return { ok: false, error: appwriteError(e) };
|
||||
}
|
||||
|
||||
return { ok: true };
|
||||
}
|
||||
Reference in New Issue
Block a user