ignore folder
This commit is contained in:
87
store/authStore.ts
Normal file
87
store/authStore.ts
Normal 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
16
store/permissionStore.ts
Normal 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
31
store/tenantStore.ts
Normal 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
38
store/uiStore.ts
Normal 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);
|
||||
Reference in New Issue
Block a user