feat: add org ID cookie management and logo upload/remove hooks
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
acc83401f4
commit
2e9ab46be8
2 changed files with 93 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ import { useState } from "react";
|
|||
import { useNavigate } from "react-router-dom";
|
||||
import { match } from "ts-pattern";
|
||||
import { api } from "../lib/api";
|
||||
import { clearOrgIdCookie } from "./organization";
|
||||
import {
|
||||
DEFAULT_SIGNUP_BILLING_INTENT,
|
||||
PENDING_BILLING_CHECKOUT_PLAN_KEY,
|
||||
|
|
@ -265,6 +266,7 @@ export function useLogout() {
|
|||
mutationFn: async () => {
|
||||
const { error } = await supabase.auth.signOut();
|
||||
if (error) throw error;
|
||||
clearOrgIdCookie();
|
||||
queryClient.removeQueries();
|
||||
},
|
||||
onSuccess: () => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { toast } from "@xtablo/shared";
|
||||
import { useEffect } from "react";
|
||||
import { useAuthedApi } from "./auth";
|
||||
|
||||
export interface OrganizationSummary {
|
||||
|
|
@ -8,6 +9,7 @@ export interface OrganizationSummary {
|
|||
plan: string;
|
||||
member_count: number;
|
||||
tablo_count: number;
|
||||
logo_url: string | null;
|
||||
}
|
||||
|
||||
export interface OrganizationMember {
|
||||
|
|
@ -51,16 +53,35 @@ export interface OrganizationInvite {
|
|||
} | null;
|
||||
}
|
||||
|
||||
function setOrgIdCookie(orgId: number): void {
|
||||
document.cookie = `x-org-id=${orgId}; path=/; secure; samesite=lax; max-age=31536000`;
|
||||
}
|
||||
|
||||
function clearOrgIdCookie(): void {
|
||||
document.cookie = "x-org-id=; path=/; secure; samesite=lax; max-age=0";
|
||||
}
|
||||
|
||||
export { clearOrgIdCookie };
|
||||
|
||||
export const useOrganization = () => {
|
||||
const api = useAuthedApi();
|
||||
|
||||
return useQuery({
|
||||
const query = useQuery({
|
||||
queryKey: ["organization"],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get<OrganizationResponse>("/api/v1/users/organization");
|
||||
return data;
|
||||
},
|
||||
});
|
||||
|
||||
// Set org ID cookie for dynamic manifest
|
||||
useEffect(() => {
|
||||
if (query.data?.organization?.id) {
|
||||
setOrgIdCookie(query.data.organization.id);
|
||||
}
|
||||
}, [query.data?.organization?.id]);
|
||||
|
||||
return query;
|
||||
};
|
||||
|
||||
export const useUpdateOrganization = () => {
|
||||
|
|
@ -143,3 +164,72 @@ export const useRemoveOrganizationMember = () => {
|
|||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useUploadOrgLogo = () => {
|
||||
const api = useAuthedApi();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (file: File) => {
|
||||
const base64Content = await new Promise<string>((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
if (typeof reader.result === "string") {
|
||||
resolve(reader.result.split(",")[1]);
|
||||
} else {
|
||||
reject(new Error("Failed to read file"));
|
||||
}
|
||||
};
|
||||
reader.onerror = () => reject(new Error("Error reading file"));
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
||||
const { data } = await api.patch("/api/v1/users/organization", {
|
||||
logo: { content: base64Content, contentType: file.type },
|
||||
});
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.add({
|
||||
title: "Logo mis à jour",
|
||||
description: "Le logo de l'organisation a bien été enregistré",
|
||||
type: "success",
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ["organization"] });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.add({
|
||||
title: "Erreur",
|
||||
description: error.message || "Impossible de mettre à jour le logo",
|
||||
type: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useRemoveOrgLogo = () => {
|
||||
const api = useAuthedApi();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async () => {
|
||||
const { data } = await api.patch("/api/v1/users/organization", { logo: null });
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.add({
|
||||
title: "Logo supprimé",
|
||||
description: "Le logo de l'organisation a été supprimé",
|
||||
type: "success",
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ["organization"] });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.add({
|
||||
title: "Erreur",
|
||||
description: error.message || "Impossible de supprimer le logo",
|
||||
type: "error",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue