ignore folder

This commit is contained in:
2026-04-21 06:30:48 +07:00
commit ca00b36f19
70 changed files with 3871 additions and 0 deletions

87
store/authStore.ts Normal file
View File

@ -0,0 +1,87 @@
import { create } from "zustand";
import { UserProfile } from "@/types/api";
import { usePermissionStore } from "./permissionStore";
export type AuthState = {
accessToken?: string;
refreshToken?: string;
tokenType?: string;
expiresInSeconds?: number;
tenantId?: string;
profile?: UserProfile;
};
type AuthAction = {
setAuthFromLogin: (payload: {
tokenType: string;
accessToken: string;
refreshToken: string;
expiresInSeconds: number;
}) => void;
setProfile: (profile: UserProfile) => void;
setTenantId: (tenantId: string) => void;
clearAuth: () => void;
hydrate: () => void;
};
const STORAGE_KEY = "utms-ng-auth";
export const useAuthStore = create<AuthState & AuthAction>((set, get) => ({
accessToken: undefined,
refreshToken: undefined,
tokenType: undefined,
expiresInSeconds: undefined,
tenantId: process.env.NEXT_PUBLIC_DEFAULT_TENANT,
profile: undefined,
setAuthFromLogin: ({ tokenType, accessToken, refreshToken, expiresInSeconds }) => {
const tenantId = get().tenantId || process.env.NEXT_PUBLIC_DEFAULT_TENANT || "acme";
const next = {
tokenType,
accessToken,
refreshToken,
expiresInSeconds,
tenantId
};
set(next);
persist(next);
},
setProfile: (profile) => {
set({ profile, tenantId: profile.tenantId });
usePermissionStore.getState().setProfile(profile.roles, profile.permissions);
const next = { ...get(), profile };
persist(next);
},
setTenantId: (tenantId) => {
set({ tenantId });
const next = { ...get(), tenantId };
persist(next);
},
clearAuth: () => {
set({
accessToken: undefined,
refreshToken: undefined,
tokenType: undefined,
expiresInSeconds: undefined,
profile: undefined
});
usePermissionStore.getState().setProfile([], []);
if (typeof window !== "undefined") localStorage.removeItem(STORAGE_KEY);
},
hydrate: () => {
if (typeof window === "undefined") return;
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return;
try {
const parsed = JSON.parse(raw) as AuthState;
if (parsed) set(parsed);
usePermissionStore.getState().setProfile(parsed?.profile?.roles ?? [], parsed?.profile?.permissions ?? []);
} catch {
localStorage.removeItem(STORAGE_KEY);
}
}
}));
function persist(state: AuthState) {
if (typeof window === "undefined") return;
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
}

16
store/permissionStore.ts Normal file
View File

@ -0,0 +1,16 @@
import { create } from "zustand";
type PermissionState = {
roles: string[];
permissions: string[];
};
type PermissionActions = {
setProfile: (roles: string[], permissions: string[]) => void;
};
export const usePermissionStore = create<PermissionState & PermissionActions>((set) => ({
roles: [],
permissions: [],
setProfile: (roles, permissions) => set({ roles, permissions })
}));

31
store/tenantStore.ts Normal file
View File

@ -0,0 +1,31 @@
import { create } from "zustand";
type TenantState = {
tenantId: string;
availableTenants: string[];
};
type TenantAction = {
setTenantId: (tenantId: string) => void;
hydrate: () => void;
};
const STORAGE_KEY = "utms-ng-tenant";
const fallbackTenants = ["acme", "global", "demo"];
export const useTenantStore = create<TenantState & TenantAction>((set, get) => ({
tenantId: process.env.NEXT_PUBLIC_DEFAULT_TENANT || "acme",
availableTenants: Array.from(
new Set([process.env.NEXT_PUBLIC_DEFAULT_TENANT || "acme", ...fallbackTenants])
),
setTenantId: (tenantId) => {
set({ tenantId });
if (typeof window !== "undefined") localStorage.setItem(STORAGE_KEY, tenantId);
},
hydrate: () => {
if (typeof window === "undefined") return;
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) set({ tenantId: raw });
else set({ tenantId: get().tenantId });
}
}));

38
store/uiStore.ts Normal file
View File

@ -0,0 +1,38 @@
import { create } from "zustand";
export type ToastType = "success" | "error" | "info";
export type ToastItem = {
id: number;
message: string;
type: ToastType;
};
type UiState = {
locale: "en" | "id";
toasts: ToastItem[];
};
type UiAction = {
setLocale: (locale: "en" | "id") => void;
addToast: (message: string, type?: ToastType) => void;
removeToast: (id: number) => void;
clearToasts: () => void;
};
let toastCounter = 1;
export const useApiStore = create<UiState & UiAction>((set) => ({
locale: "en",
toasts: [],
setLocale: (locale) => set({ locale }),
addToast: (message, type = "info") =>
set((state) => ({
toasts: [...state.toasts, { id: toastCounter++, message, type }]
})),
removeToast: (id) => set((state) => ({ toasts: state.toasts.filter((toast) => toast.id !== id) })),
clearToasts: () => set({ toasts: [] })
}));
export const useLocaleStore = <T>(selector: (s: UiState & UiAction) => T): T =>
useApiStore(selector);