From 313095984fc835fa9e97c60fbcb465d3c5d252d8 Mon Sep 17 00:00:00 2001 From: Ricky Stretch Date: Sat, 14 Mar 2026 15:58:04 -0400 Subject: [PATCH] v0.9.16 updated baranding colours --- .env.example | 2 +- backend/package.json | 2 +- build.sh | 2 +- frontend/package.json | 2 +- frontend/src/components/BrandingModal.jsx | 271 +++++++++++++--------- 5 files changed, 160 insertions(+), 119 deletions(-) diff --git a/.env.example b/.env.example index d9c2521..69cef98 100644 --- a/.env.example +++ b/.env.example @@ -10,7 +10,7 @@ PROJECT_NAME=jama # Image version to run (set by build.sh, or use 'latest') -JAMA_VERSION=0.9.15 +JAMA_VERSION=0.9.16 # App port — the host port Docker maps to the container PORT=3000 diff --git a/backend/package.json b/backend/package.json index 6995f75..b897f61 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "jama-backend", - "version": "0.9.15", + "version": "0.9.16", "description": "TeamChat backend server", "main": "src/index.js", "scripts": { diff --git a/build.sh b/build.sh index f7fe532..3a0ba63 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.9.15}" +VERSION="${1:-0.9.16}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="jama" diff --git a/frontend/package.json b/frontend/package.json index 5d9313f..e322eb5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "jama-frontend", - "version": "0.9.15", + "version": "0.9.16", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/components/BrandingModal.jsx b/frontend/src/components/BrandingModal.jsx index caee937..97dbee5 100644 --- a/frontend/src/components/BrandingModal.jsx +++ b/frontend/src/components/BrandingModal.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { api } from '../utils/api.js'; import { useToast } from '../contexts/ToastContext.jsx'; @@ -6,46 +6,124 @@ const DEFAULT_TITLE_COLOR = '#1a73e8'; const DEFAULT_PUBLIC_COLOR = '#1a73e8'; const DEFAULT_DM_COLOR = '#a142f4'; -function ColorSwatch({ color, title }) { +const COLOUR_SUGGESTIONS = [ + '#1a73e8','#a142f4','#e53935','#34a853','#fa7b17', + '#00897b','#e91e8c','#0097a7','#5c6bc0','#f4511e', + '#616161','#795548','#00acc1','#43a047','#fdd835', +]; + +// A single colour picker card: shows suggestions by default, Custom button opens native picker +function ColourPicker({ label, description, value, onChange, preview }) { + const [mode, setMode] = useState('suggestions'); // 'suggestions' | 'custom' + const inputRef = useRef(null); + + const handleCustomClick = () => { + setMode('custom'); + // Open native colour picker immediately after switching + setTimeout(() => inputRef.current?.click(), 50); + }; + return ( -
+
+
{label}
+ {description && ( +

{description}

+ )} + + {/* Current colour preview */} +
+ {preview + ? preview(value) + :
+ } + {value} +
+ + {mode === 'suggestions' && ( + <> + {/* Suggestion swatches */} +
+ {COLOUR_SUGGESTIONS.map(hex => ( +
+ {/* Custom button — styled to match suggestion swatches row */} + + + )} + + {mode === 'custom' && ( +
+
+ onChange(e.target.value)} + style={{ + width: 56, height: 44, padding: 2, + borderRadius: 8, border: '1px solid var(--border)', + cursor: 'pointer', background: 'none', + }} + /> + {value} +
+ {/* Back button — same style as Custom button */} +
+ +
+
+ )} +
); } export default function BrandingModal({ onClose }) { const toast = useToast(); - const [tab, setTab] = useState('general'); // 'general' | 'colors' + 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); - // Color state - const [colorTitle, setColorTitle] = useState(DEFAULT_TITLE_COLOR); - const [colorPublic, setColorPublic] = useState(DEFAULT_PUBLIC_COLOR); - const [colorDm, setColorDm] = useState(DEFAULT_DM_COLOR); - const [savingColors, setSavingColors] = 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'); - setColorTitle(settings.color_title || DEFAULT_TITLE_COLOR); - setColorPublic(settings.color_avatar_public || DEFAULT_PUBLIC_COLOR); - setColorDm(settings.color_avatar_dm || DEFAULT_DM_COLOR); + 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 notifySidebarRefresh = () => window.dispatchEvent(new Event('jama:settings-changed')); const handleSaveName = async () => { if (!appName.trim()) return; @@ -76,26 +154,26 @@ export default function BrandingModal({ onClose }) { } }; - const handleSaveColors = async () => { - setSavingColors(true); + const handleSaveColours = async () => { + setSavingColours(true); try { await api.updateColors({ - colorTitle, - colorAvatarPublic: colorPublic, - colorAvatarDm: colorDm, + colorTitle: colourTitle, + colorAvatarPublic: colourPublic, + colorAvatarDm: colourDm, }); setSettings(prev => ({ ...prev, - color_title: colorTitle, - color_avatar_public: colorPublic, - color_avatar_dm: colorDm, + color_title: colourTitle, + color_avatar_public: colourPublic, + color_avatar_dm: colourDm, })); - toast('Colors updated', 'success'); + toast('Colours updated', 'success'); notifySidebarRefresh(); } catch (e) { toast(e.message, 'error'); } finally { - setSavingColors(false); + setSavingColours(false); } }; @@ -106,9 +184,9 @@ export default function BrandingModal({ onClose }) { const { settings: fresh } = await api.getSettings(); setSettings(fresh); setAppName(fresh.app_name || 'jama'); - setColorTitle(DEFAULT_TITLE_COLOR); - setColorPublic(DEFAULT_PUBLIC_COLOR); - setColorDm(DEFAULT_DM_COLOR); + setColourTitle(DEFAULT_TITLE_COLOR); + setColourPublic(DEFAULT_PUBLIC_COLOR); + setColourDm(DEFAULT_DM_COLOR); toast('Settings reset to defaults', 'success'); notifySidebarRefresh(); setShowResetConfirm(false); @@ -132,7 +210,7 @@ export default function BrandingModal({ onClose }) { {/* Tabs */}
- +
{tab === 'general' && ( @@ -146,11 +224,7 @@ export default function BrandingModal({ onClose }) { border: '1px solid var(--border)', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}> - logo + logo
- {/* Reset + Version */} + {/* Reset */}
Reset
{!showResetConfirm ? ( - + ) : ( -
+

This will reset the app name, logo and all colours to their install defaults. This cannot be undone.

@@ -200,9 +267,7 @@ export default function BrandingModal({ onClose }) {
)} {settings.app_version && ( - - v{settings.app_version} - + v{settings.app_version} )}
@@ -216,74 +281,50 @@ export default function BrandingModal({ onClose }) { )} - {tab === 'colors' && ( + {tab === 'colours' && (
- {/* App Title Color */} -
-
App Title Color
-

- The color of the app name shown in the top bar. -

-
- setColorTitle(e.target.value)} - style={{ width: 48, height: 40, padding: 2, borderRadius: 8, border: '1px solid var(--border)', cursor: 'pointer', background: 'none' }} - /> - - {colorTitle} - -
+ + +
+ ( +
A
+ )} + />
-
Public Message Avatar Color
-

- Background color for public channel avatars (users without a custom avatar). -

-
- setColorPublic(e.target.value)} - style={{ width: 48, height: 40, padding: 2, borderRadius: 8, border: '1px solid var(--border)', cursor: 'pointer', background: 'none' }} - /> -
A
- {colorPublic} - -
+ ( +
B
+ )} + />
-
Direct Message Avatar Color
-

- Background color for private group and direct message avatars (users without a custom avatar). -

-
- setColorDm(e.target.value)} - style={{ width: 48, height: 40, padding: 2, borderRadius: 8, border: '1px solid var(--border)', cursor: 'pointer', background: 'none' }} - /> -
B
- {colorDm} - -
-
- -
-