/ frontend / src / app / contexts / TeamBrandingContext.js
TeamBrandingContext.js
 1  import { createContext, useContext, useMemo, useState, useCallback } from "react";
 2  import useAuth from "app/hooks/useAuth";
 3  import api from "app/utils/api";
 4  
 5  const TeamBrandingContext = createContext({
 6    branding: null,
 7    teamName: null,
 8    activeTeamId: null,
 9    brandedTeams: [],
10    setActiveTeamId: () => {},
11  });
12  
13  function findBrandedTeam(teams, preferredId) {
14    if (!teams || teams.length === 0) return null;
15  
16    // If user has a preference, use it
17    if (preferredId) {
18      const preferred = teams.find((t) => t.id === preferredId);
19      if (preferred) return preferred;
20    }
21  
22    // Fall back to first team that has branding configured
23    const withBranding = teams.find(
24      (t) => t.branding && (t.branding.primary_color || t.branding.secondary_color || t.branding.logo_url || t.branding.app_name)
25    );
26    if (withBranding) return withBranding;
27  
28    // No team has branding
29    return null;
30  }
31  
32  export function TeamBrandingProvider({ children }) {
33    const { user, isAuthenticated } = useAuth();
34    const [overrideTeamId, setOverrideTeamId] = useState(null);
35  
36    const setActiveTeamId = useCallback((teamId) => {
37      setOverrideTeamId(teamId);
38      // Persist preference to backend
39      if (user && user.token) {
40        api.patch("/users/" + user.username, { options: { preferred_team_id: teamId } }, user.token).catch(() => {});
41      }
42    }, [user]);
43  
44    const value = useMemo(() => {
45      if (!isAuthenticated || !user) {
46        return { branding: null, teamName: null, activeTeamId: null, brandedTeams: [], setActiveTeamId };
47      }
48  
49      const allTeams = [...(user.teams || []), ...(user.admin_teams || [])];
50      // Deduplicate by id
51      const seen = new Set();
52      const uniqueTeams = allTeams.filter((t) => {
53        if (seen.has(t.id)) return false;
54        seen.add(t.id);
55        return true;
56      });
57  
58      // Teams that have any branding configured
59      const brandedTeams = uniqueTeams.filter(
60        (t) => t.branding && (t.branding.primary_color || t.branding.secondary_color || t.branding.logo_url || t.branding.app_name)
61      );
62  
63      const preferredId = overrideTeamId || user.options?.preferred_team_id;
64      const activeTeam = findBrandedTeam(uniqueTeams, preferredId);
65  
66      const branding = activeTeam?.branding || null;
67      const hasAny = branding && (branding.primary_color || branding.secondary_color || branding.logo_url || branding.app_name || branding.welcome_message);
68  
69      return {
70        branding: hasAny ? branding : null,
71        teamName: activeTeam?.name || null,
72        activeTeamId: activeTeam?.id || null,
73        brandedTeams,
74        setActiveTeamId,
75      };
76    }, [isAuthenticated, user, overrideTeamId, setActiveTeamId]);
77  
78    return (
79      <TeamBrandingContext.Provider value={value}>
80        {children}
81      </TeamBrandingContext.Provider>
82    );
83  }
84  
85  export function useTeamBranding() {
86    return useContext(TeamBrandingContext);
87  }