import { useState, useEffect, useRef } from 'react';
import { api } from '../utils/api.js';
import { useToast } from '../contexts/ToastContext.jsx';
const DEFAULT_TITLE_COLOR = '#1a73e8';
const DEFAULT_PUBLIC_COLOR = '#1a73e8';
const DEFAULT_DM_COLOR = '#a142f4';
const COLOUR_SUGGESTIONS = [
'#1a73e8', '#a142f4', '#e53935', '#fa7b17', '#fdd835', '#34a853',
];
// A single colour picker card: shows suggestions by default, Custom button opens native picker
function ColourPicker({ label, value, onChange, preview }) {
const [mode, setMode] = useState('suggestions'); // 'suggestions' | 'custom'
const inputRef = useRef(null);
// Auto-close custom mode as soon as a new colour is committed
const handleCustomChange = (e) => {
onChange(e.target.value);
// 'change' fires on close of native picker on most platforms;
// we also listen to 'input' for live preview but only close on 'change'
};
const handleCustomInput = (e) => {
onChange(e.target.value);
};
const handleCustomChangeClose = (e) => {
onChange(e.target.value);
setMode('suggestions');
};
return (
{label}
{/* Current colour preview */}
{preview
? preview(value)
:
}
{value}
{mode === 'suggestions' && (
<>
{COLOUR_SUGGESTIONS.map(hex => (
>
)}
{mode === 'custom' && (
)}
);
}
export default function BrandingModal({ onClose }) {
const toast = useToast();
const [tab, setTab] = useState('general'); // 'general' | 'colours'
const [settings, setSettings] = useState({});
const [appName, setAppName] = useState('');
const [loading, setLoading] = useState(false);
const [resetting, setResetting] = useState(false);
const [showResetConfirm, setShowResetConfirm] = useState(false);
const [colourTitle, setColourTitle] = useState(DEFAULT_TITLE_COLOR);
const [colourPublic, setColourPublic] = useState(DEFAULT_PUBLIC_COLOR);
const [colourDm, setColourDm] = useState(DEFAULT_DM_COLOR);
const [savingColours, setSavingColours] = useState(false);
useEffect(() => {
api.getSettings().then(({ settings }) => {
setSettings(settings);
setAppName(settings.app_name || 'jama');
setColourTitle(settings.color_title || DEFAULT_TITLE_COLOR);
setColourPublic(settings.color_avatar_public || DEFAULT_PUBLIC_COLOR);
setColourDm(settings.color_avatar_dm || DEFAULT_DM_COLOR);
}).catch(() => {});
}, []);
const notifySidebarRefresh = () => window.dispatchEvent(new Event('jama:settings-changed'));
const handleSaveName = async () => {
if (!appName.trim()) return;
setLoading(true);
try {
await api.updateAppName(appName.trim());
setSettings(prev => ({ ...prev, app_name: appName.trim() }));
toast('App name updated', 'success');
notifySidebarRefresh();
} catch (e) {
toast(e.message, 'error');
} finally {
setLoading(false);
}
};
const handleLogoUpload = async (e) => {
const file = e.target.files?.[0];
if (!file) return;
if (file.size > 1024 * 1024) return toast('Logo must be less than 1MB', 'error');
try {
const { logoUrl } = await api.uploadLogo(file);
setSettings(prev => ({ ...prev, logo_url: logoUrl }));
toast('Logo updated', 'success');
notifySidebarRefresh();
} catch (e) {
toast(e.message, 'error');
}
};
const handleSaveColours = async () => {
setSavingColours(true);
try {
await api.updateColors({
colorTitle: colourTitle,
colorAvatarPublic: colourPublic,
colorAvatarDm: colourDm,
});
setSettings(prev => ({
...prev,
color_title: colourTitle,
color_avatar_public: colourPublic,
color_avatar_dm: colourDm,
}));
toast('Colours updated', 'success');
notifySidebarRefresh();
} catch (e) {
toast(e.message, 'error');
} finally {
setSavingColours(false);
}
};
const handleReset = async () => {
setResetting(true);
try {
await api.resetSettings();
const { settings: fresh } = await api.getSettings();
setSettings(fresh);
setAppName(fresh.app_name || 'jama');
setColourTitle(DEFAULT_TITLE_COLOR);
setColourPublic(DEFAULT_PUBLIC_COLOR);
setColourDm(DEFAULT_DM_COLOR);
toast('Settings reset to defaults', 'success');
notifySidebarRefresh();
setShowResetConfirm(false);
} catch (e) {
toast(e.message, 'error');
} finally {
setResetting(false);
}
};
return (
e.target === e.currentTarget && onClose()}>
Branding
{/* Tabs */}
{tab === 'general' && (
<>
{/* App Logo */}
{/* App Name */}
{/* Reset */}
Reset
{!showResetConfirm ? (
) : (
This will reset the app name, logo and all colours to their install defaults. This cannot be undone.
)}
{settings.app_version && (
v{settings.app_version}
)}
{settings.pw_reset_active === 'true' && (
⚠️
ADMPW_RESET is active. The default admin password is being reset on every restart. Set ADMPW_RESET=false in your environment variables to stop this.
)}
>
)}
{tab === 'colours' && (
)}
);
}