ultisuite-client/lib/contacts/contacts-store.ts
R3D347HR4Y 07d57f13a8
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
Add Contact Avatar Features and Improve UI Components
- Introduced new ContactAvatar and ContactAvatarPicker components for enhanced avatar management in contact views.
- Updated ContactDetailView and ContactFormView to utilize the new avatar components, improving user experience when adding or editing contacts.
- Enhanced ContactHoverCard and ContactRow components to display avatars, providing a more visually appealing interface.
- Added loading and error states in ContactsListView for better user feedback during data fetching.
- Implemented a new ContactsLoadState component to handle loading and error scenarios in the contacts list.
- Updated package.json to include @formkit/auto-animate for improved UI animations.
2026-06-06 20:26:51 +02:00

168 lines
4.5 KiB
TypeScript

"use client"
import { create } from "zustand"
import { persist } from "zustand/middleware"
import { debouncedPersistJSONStorage } from "@/lib/stores/debounced-json-storage"
import type { FullContact } from "./types"
import { mergePairKey } from "./duplicate-detection"
type ContactsView = "list" | "view" | "create" | "edit"
export type ContactCreateDraft = {
firstName?: string
lastName?: string
emails?: { value: string; label: string }[]
}
export interface DeletedContact {
contact: FullContact
deletedAt: number
reason: string
}
interface ContactsState {
deletedContacts: DeletedContact[]
ignoredMergePairs: string[]
panelOpen: boolean
view: ContactsView
activeContactId: string | null
searchQuery: string
searchMode: boolean
createDraft: ContactCreateDraft | null
}
interface ContactsActions {
togglePanel: () => void
openPanel: () => void
closePanel: () => void
openContactDetail: (contactId: string) => void
openCreateContact: (draft?: ContactCreateDraft | null) => void
clearCreateDraft: () => void
setView: (view: ContactsView, activeContactId?: string | null) => void
showContactsList: () => void
setSearchQuery: (q: string) => void
setSearchMode: (active: boolean) => void
softDeleteContact: (contact: FullContact, reason?: string) => void
restoreContact: (id: string) => void
emptyTrash: () => void
ignoreMergePair: (idA: string, idB: string) => void
}
export type ContactsStore = ContactsState & ContactsActions
export const useContactsStore = create<ContactsStore>()(
persist(
(set) => ({
deletedContacts: [],
ignoredMergePairs: [],
panelOpen: false,
view: "list",
activeContactId: null,
searchQuery: "",
searchMode: false,
createDraft: null,
togglePanel: () =>
set((s) =>
s.panelOpen
? {
panelOpen: false,
view: "list",
activeContactId: null,
searchQuery: "",
searchMode: false,
createDraft: null,
}
: { panelOpen: true }
),
openPanel: () => set({ panelOpen: true }),
closePanel: () =>
set({
panelOpen: false,
view: "list",
activeContactId: null,
searchQuery: "",
searchMode: false,
createDraft: null,
}),
openContactDetail: (contactId) =>
set({
panelOpen: true,
view: "view",
activeContactId: contactId,
searchQuery: "",
searchMode: false,
createDraft: null,
}),
openCreateContact: (draft = null) =>
set({
panelOpen: true,
view: "create",
activeContactId: null,
searchQuery: "",
searchMode: false,
createDraft: draft,
}),
clearCreateDraft: () => set({ createDraft: null }),
setView: (view, activeContactId = null) =>
set({ view, activeContactId, createDraft: null }),
showContactsList: () =>
set({
view: "list",
activeContactId: null,
searchQuery: "",
searchMode: false,
createDraft: null,
}),
setSearchQuery: (searchQuery) => set({ searchQuery }),
setSearchMode: (searchMode) =>
set(searchMode ? { searchMode } : { searchMode, searchQuery: "" }),
softDeleteContact: (contact, reason = "Supprimé manuellement") =>
set((s) => ({
deletedContacts: [
...s.deletedContacts,
{ contact, deletedAt: Date.now(), reason },
],
activeContactId: s.activeContactId === contact.id ? null : s.activeContactId,
view: s.activeContactId === contact.id ? "list" : s.view,
})),
restoreContact: (id) =>
set((s) => {
const entry = s.deletedContacts.find((d) => d.contact.id === id)
if (!entry) return s
return {
deletedContacts: s.deletedContacts.filter((d) => d.contact.id !== id),
}
}),
emptyTrash: () => set({ deletedContacts: [] }),
ignoreMergePair: (idA, idB) =>
set((s) => {
const key = mergePairKey(idA, idB)
if (s.ignoredMergePairs.includes(key)) return s
return { ignoredMergePairs: [...s.ignoredMergePairs, key] }
}),
}),
{
name: "contacts-store",
storage: debouncedPersistJSONStorage,
partialize: (state) => ({
deletedContacts: state.deletedContacts,
ignoredMergePairs: state.ignoredMergePairs,
}),
}
)
)