update global (texte + logo)

This commit is contained in:
2024-04-18 17:19:24 +02:00
parent f9d05a2fd3
commit 1c73080fe3
307 changed files with 28214 additions and 105 deletions

View File

@ -0,0 +1,41 @@
import { create } from "zustand";
import { AccountSettings } from "@/types/global";
type ResponseObject = {
ok: boolean;
data: Omit<AccountSettings, "password"> | object | string;
};
type AccountStore = {
account: AccountSettings;
setAccount: (id: number) => void;
updateAccount: (user: AccountSettings) => Promise<ResponseObject>;
};
const useAccountStore = create<AccountStore>()((set) => ({
account: {} as AccountSettings,
setAccount: async (id) => {
const response = await fetch(`/api/v1/users/${id}`);
const data = await response.json();
if (response.ok) set({ account: { ...data.response } });
},
updateAccount: async (user) => {
const response = await fetch(`/api/v1/users/${user.id}`, {
method: "PUT",
body: JSON.stringify(user),
headers: {
"Content-Type": "application/json",
},
});
const data = await response.json();
if (response.ok) set({ account: { ...data.response } });
return { ok: response.ok, data: data.response };
},
}));
export default useAccountStore;

View File

@ -0,0 +1,94 @@
import { create } from "zustand";
import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
import useTagStore from "./tags";
type ResponseObject = {
ok: boolean;
data: object | string;
};
type CollectionStore = {
collections: CollectionIncludingMembersAndLinkCount[];
setCollections: () => void;
addCollection: (
body: CollectionIncludingMembersAndLinkCount
) => Promise<ResponseObject>;
updateCollection: (
collection: CollectionIncludingMembersAndLinkCount
) => Promise<ResponseObject>;
removeCollection: (collectionId: number) => Promise<ResponseObject>;
};
const useCollectionStore = create<CollectionStore>()((set) => ({
collections: [],
setCollections: async () => {
const response = await fetch("/api/v1/collections");
const data = await response.json();
if (response.ok) set({ collections: data.response });
},
addCollection: async (body) => {
const response = await fetch("/api/v1/collections", {
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
const data = await response.json();
if (response.ok)
set((state) => ({
collections: [...state.collections, data.response],
}));
return { ok: response.ok, data: data.response };
},
updateCollection: async (collection) => {
const response = await fetch(`/api/v1/collections/${collection.id}`, {
body: JSON.stringify(collection),
headers: {
"Content-Type": "application/json",
},
method: "PUT",
});
const data = await response.json();
if (response.ok)
set((state) => ({
collections: state.collections.map((e) =>
e.id === data.response.id ? data.response : e
),
}));
return { ok: response.ok, data: data.response };
},
removeCollection: async (collectionId) => {
const response = await fetch(`/api/v1/collections/${collectionId}`, {
headers: {
"Content-Type": "application/json",
},
method: "DELETE",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
collections: state.collections.filter(
(collection) =>
collection.id !== collectionId &&
collection.parentId !== collectionId
),
}));
useTagStore.getState().setTags();
}
return { ok: response.ok, data: data.response };
},
}));
export default useCollectionStore;

217
Linkwarden/store/links.ts Normal file
View File

@ -0,0 +1,217 @@
import { create } from "zustand";
import { LinkIncludingShortenedCollectionAndTags } from "@/types/global";
import useTagStore from "./tags";
import useCollectionStore from "./collections";
type ResponseObject = {
ok: boolean;
data: object | string;
};
type LinkStore = {
links: LinkIncludingShortenedCollectionAndTags[];
selectedLinks: LinkIncludingShortenedCollectionAndTags[];
setLinks: (
data: LinkIncludingShortenedCollectionAndTags[],
isInitialCall: boolean
) => void;
setSelectedLinks: (links: LinkIncludingShortenedCollectionAndTags[]) => void;
addLink: (
body: LinkIncludingShortenedCollectionAndTags
) => Promise<ResponseObject>;
getLink: (linkId: number, publicRoute?: boolean) => Promise<ResponseObject>;
updateLink: (
link: LinkIncludingShortenedCollectionAndTags
) => Promise<ResponseObject>;
updateLinks: (
links: LinkIncludingShortenedCollectionAndTags[],
removePreviousTags: boolean,
newData: Pick<
LinkIncludingShortenedCollectionAndTags,
"tags" | "collectionId"
>
) => Promise<ResponseObject>;
removeLink: (linkId: number) => Promise<ResponseObject>;
deleteLinksById: (linkIds: number[]) => Promise<ResponseObject>;
resetLinks: () => void;
};
const useLinkStore = create<LinkStore>()((set) => ({
links: [],
selectedLinks: [],
setLinks: async (data, isInitialCall) => {
isInitialCall &&
set(() => ({
links: [],
}));
set((state) => ({
// Filter duplicate links by id
links: [...state.links, ...data].reduce(
(links: LinkIncludingShortenedCollectionAndTags[], item) => {
if (!links.some((link) => link.id === item.id)) {
links.push(item);
}
return links;
},
[]
),
}));
},
setSelectedLinks: (links) => set({ selectedLinks: links }),
addLink: async (body) => {
const response = await fetch("/api/v1/links", {
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
links: [data.response, ...state.links],
}));
useTagStore.getState().setTags();
useCollectionStore.getState().setCollections();
}
return { ok: response.ok, data: data.response };
},
getLink: async (linkId, publicRoute) => {
const path = publicRoute
? `/api/v1/public/links/${linkId}`
: `/api/v1/links/${linkId}`;
const response = await fetch(path);
const data = await response.json();
if (response.ok) {
set((state) => {
const linkExists = state.links.some(
(link) => link.id === data.response.id
);
if (linkExists) {
return {
links: state.links.map((e) =>
e.id === data.response.id ? data.response : e
),
};
} else {
return {
links: [...state.links, data.response],
};
}
});
return data;
}
return { ok: response.ok, data: data.response };
},
updateLink: async (link) => {
const response = await fetch(`/api/v1/links/${link.id}`, {
body: JSON.stringify(link),
headers: {
"Content-Type": "application/json",
},
method: "PUT",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
links: state.links.map((e) =>
e.id === data.response.id ? data.response : e
),
}));
useTagStore.getState().setTags();
useCollectionStore.getState().setCollections();
}
return { ok: response.ok, data: data.response };
},
updateLinks: async (links, removePreviousTags, newData) => {
const response = await fetch("/api/v1/links", {
body: JSON.stringify({ links, removePreviousTags, newData }),
headers: {
"Content-Type": "application/json",
},
method: "PUT",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
links: state.links.map((e) =>
links.some((link) => link.id === e.id)
? {
...e,
collectionId: newData.collectionId ?? e.collectionId,
collection: {
...e.collection,
id: newData.collectionId ?? e.collection.id,
},
tags: removePreviousTags
? [...(newData.tags ?? [])]
: [...e.tags, ...(newData.tags ?? [])],
}
: e
),
}));
useTagStore.getState().setTags();
useCollectionStore.getState().setCollections();
}
return { ok: response.ok, data: data.response };
},
removeLink: async (linkId) => {
const response = await fetch(`/api/v1/links/${linkId}`, {
headers: {
"Content-Type": "application/json",
},
method: "DELETE",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
links: state.links.filter((e) => e.id !== linkId),
}));
useTagStore.getState().setTags();
useCollectionStore.getState().setCollections();
}
return { ok: response.ok, data: data.response };
},
deleteLinksById: async (linkIds: number[]) => {
const response = await fetch("/api/v1/links", {
body: JSON.stringify({ linkIds }),
headers: {
"Content-Type": "application/json",
},
method: "DELETE",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
links: state.links.filter((e) => !linkIds.includes(e.id as number)),
}));
useTagStore.getState().setTags();
useCollectionStore.getState().setCollections();
}
return { ok: response.ok, data: data.response };
},
resetLinks: () => set({ links: [] }),
}));
export default useLinkStore;

View File

@ -0,0 +1,57 @@
import { create } from "zustand";
type LocalSettings = {
theme?: string;
viewMode?: string;
};
type LocalSettingsStore = {
settings: LocalSettings;
updateSettings: (settings: LocalSettings) => void;
setSettings: () => void;
};
const useLocalSettingsStore = create<LocalSettingsStore>((set) => ({
settings: {
theme: "",
viewMode: "",
},
updateSettings: async (newSettings) => {
if (
newSettings.theme &&
newSettings.theme !== localStorage.getItem("theme")
) {
localStorage.setItem("theme", newSettings.theme);
const localTheme = localStorage.getItem("theme") || "";
document.querySelector("html")?.setAttribute("data-theme", localTheme);
}
if (
newSettings.viewMode &&
newSettings.viewMode !== localStorage.getItem("viewMode")
) {
localStorage.setItem("viewMode", newSettings.viewMode);
// const localTheme = localStorage.getItem("viewMode") || "";
}
set((state) => ({ settings: { ...state.settings, ...newSettings } }));
},
setSettings: async () => {
if (!localStorage.getItem("theme")) {
localStorage.setItem("theme", "dark");
}
const localTheme = localStorage.getItem("theme") || "";
set((state) => ({
settings: { ...state.settings, theme: localTheme },
}));
document.querySelector("html")?.setAttribute("data-theme", localTheme);
},
}));
export default useLocalSettingsStore;

View File

@ -0,0 +1,64 @@
import {
CollectionIncludingMembersAndLinkCount,
LinkIncludingShortenedCollectionAndTags,
} from "@/types/global";
import { create } from "zustand";
type Modal =
| {
modal: "LINK";
state: boolean;
method: "CREATE";
active?: LinkIncludingShortenedCollectionAndTags;
}
| {
modal: "LINK";
state: boolean;
method: "UPDATE";
active: LinkIncludingShortenedCollectionAndTags;
}
| {
modal: "LINK";
state: boolean;
method: "FORMATS";
active: LinkIncludingShortenedCollectionAndTags;
}
| {
modal: "COLLECTION";
state: boolean;
method: "UPDATE";
isOwner: boolean;
active: CollectionIncludingMembersAndLinkCount;
defaultIndex?: number;
}
| {
modal: "COLLECTION";
state: boolean;
method: "CREATE";
isOwner?: boolean;
active?: CollectionIncludingMembersAndLinkCount;
defaultIndex?: number;
}
| {
modal: "COLLECTION";
state: boolean;
method: "VIEW_TEAM";
isOwner?: boolean;
active?: CollectionIncludingMembersAndLinkCount;
defaultIndex?: number;
}
| null;
type ModalsStore = {
modal: Modal;
setModal: (modal: Modal) => void;
};
const useModalStore = create<ModalsStore>((set) => ({
modal: null,
setModal: (modal: Modal) => {
set({ modal });
},
}));
export default useModalStore;

62
Linkwarden/store/tags.ts Normal file
View File

@ -0,0 +1,62 @@
import { create } from "zustand";
import { TagIncludingLinkCount } from "@/types/global";
type ResponseObject = {
ok: boolean;
data: object | string;
};
type TagStore = {
tags: TagIncludingLinkCount[];
setTags: () => void;
updateTag: (tag: TagIncludingLinkCount) => Promise<ResponseObject>;
removeTag: (tagId: number) => Promise<ResponseObject>;
};
const useTagStore = create<TagStore>()((set) => ({
tags: [],
setTags: async () => {
const response = await fetch("/api/v1/tags");
const data = await response.json();
if (response.ok) set({ tags: data.response });
},
updateTag: async (tag) => {
const response = await fetch(`/api/v1/tags/${tag.id}`, {
body: JSON.stringify(tag),
headers: {
"Content-Type": "application/json",
},
method: "PUT",
});
const data = await response.json();
if (response.ok) {
set((state) => ({
tags: state.tags.map((e) =>
e.id === data.response.id ? data.response : e
),
}));
}
return { ok: response.ok, data: data.response };
},
removeTag: async (tagId) => {
const response = await fetch(`/api/v1/tags/${tagId}`, {
method: "DELETE",
});
if (response.ok) {
set((state) => ({
tags: state.tags.filter((e) => e.id !== tagId),
}));
}
const data = await response.json();
return { ok: response.ok, data: data.response };
},
}));
export default useTagStore;

View File

@ -0,0 +1,56 @@
import { AccessToken } from "@prisma/client";
import { create } from "zustand";
// Token store
type ResponseObject = {
ok: boolean;
data: object | string;
};
type TokenStore = {
tokens: Partial<AccessToken>[];
setTokens: (data: Partial<AccessToken>[]) => void;
addToken: (body: Partial<AccessToken>[]) => Promise<ResponseObject>;
revokeToken: (tokenId: number) => Promise<ResponseObject>;
};
const useTokenStore = create<TokenStore>((set) => ({
tokens: [],
setTokens: async (data) => {
set(() => ({
tokens: data,
}));
},
addToken: async (body) => {
const response = await fetch("/api/v1/tokens", {
body: JSON.stringify(body),
method: "POST",
});
const data = await response.json();
if (response.ok)
set((state) => ({
tokens: [...state.tokens, data.response.token],
}));
return { ok: response.ok, data: data.response };
},
revokeToken: async (tokenId) => {
const response = await fetch(`/api/v1/tokens/${tokenId}`, {
method: "DELETE",
});
const data = await response.json();
if (response.ok)
set((state) => ({
tokens: state.tokens.filter((token) => token.id !== tokenId),
}));
return { ok: response.ok, data: data.response };
},
}));
export default useTokenStore;