feat: CalenTIQ Admin branding (wordmark, favicon, titles). Refs EventHub/EventHubSpec#10
CI / test (push) Successful in 47s
CI / push-image (push) Successful in 1m46s
CI / deploy-ift (push) Successful in 7s
CI / ci-done (push) Successful in 0s
CI / e2e-ift (push) Successful in 20s
CI / deploy-stage (push) Successful in 1m29s
CI / e2e-stage (push) Successful in 23s
CI / test (push) Successful in 47s
CI / push-image (push) Successful in 1m46s
CI / deploy-ift (push) Successful in 7s
CI / ci-done (push) Successful in 0s
CI / e2e-ift (push) Successful in 20s
CI / deploy-stage (push) Successful in 1m29s
CI / e2e-stage (push) Successful in 23s
This commit is contained in:
+2
-1
@@ -3,8 +3,9 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<link rel="alternate icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>EventHub Admin</title>
|
<title>CalenTIQ Admin</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" fill="none">
|
||||||
|
<g stroke="#FFFFFF" stroke-width="48" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M512 132 A380 380 0 1 0 512 892"/>
|
||||||
|
<path d="M512 512 L377.77 434.50"/>
|
||||||
|
<path d="M512 512 L698.20 404.50"/>
|
||||||
|
</g>
|
||||||
|
<circle cx="512" cy="512" r="24" fill="#FFFFFF"/>
|
||||||
|
<circle cx="816.06" cy="207.94" r="18" fill="#FFFFFF"/>
|
||||||
|
<circle cx="884.39" cy="297.00" r="18" fill="#FFFFFF"/>
|
||||||
|
<circle cx="927.35" cy="400.71" r="18" fill="#FFFFFF"/>
|
||||||
|
<circle cx="942.00" cy="512.00" r="18" fill="#FFFFFF"/>
|
||||||
|
<circle cx="927.35" cy="623.29" r="18" fill="#FFFFFF"/>
|
||||||
|
<circle cx="884.39" cy="727.00" r="18" fill="#FFFFFF"/>
|
||||||
|
<circle cx="816.06" cy="816.06" r="18" fill="#FFFFFF"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 765 B |
@@ -0,0 +1,15 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" fill="none">
|
||||||
|
<g stroke="#000000" stroke-width="48" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M512 132 A380 380 0 1 0 512 892"/>
|
||||||
|
<path d="M512 512 L377.77 434.50"/>
|
||||||
|
<path d="M512 512 L698.20 404.50"/>
|
||||||
|
</g>
|
||||||
|
<circle cx="512" cy="512" r="24" fill="#000000"/>
|
||||||
|
<circle cx="816.06" cy="207.94" r="18" fill="#000000"/>
|
||||||
|
<circle cx="884.39" cy="297.00" r="18" fill="#000000"/>
|
||||||
|
<circle cx="927.35" cy="400.71" r="18" fill="#000000"/>
|
||||||
|
<circle cx="942.00" cy="512.00" r="18" fill="#000000"/>
|
||||||
|
<circle cx="927.35" cy="623.29" r="18" fill="#000000"/>
|
||||||
|
<circle cx="884.39" cy="727.00" r="18" fill="#000000"/>
|
||||||
|
<circle cx="816.06" cy="816.06" r="18" fill="#000000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 765 B |
Binary file not shown.
|
After Width: | Height: | Size: 917 B |
+15
-1
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 765 B |
@@ -0,0 +1,75 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
type Size = 'sm' | 'md' | 'lg';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
size?: Size;
|
||||||
|
className?: string;
|
||||||
|
/** Show Time Arc mark next to wordmark (default true). */
|
||||||
|
withMark?: boolean;
|
||||||
|
/** Append muted "Admin" after the brand (sidebar / login). */
|
||||||
|
withAdmin?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sizeMap: Record<Size, { mark: string; text: string; admin: string }> = {
|
||||||
|
sm: { mark: 'h-5 w-5', text: 'text-sm', admin: 'text-[11px]' },
|
||||||
|
md: { mark: 'h-7 w-7', text: 'text-2xl', admin: 'text-sm' },
|
||||||
|
lg: { mark: 'h-10 w-10', text: 'text-4xl', admin: 'text-base' },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public brand lockup: Time Arc + CalenTIQ (Admin).
|
||||||
|
* Uses currentColor so the mark follows theme foreground.
|
||||||
|
*/
|
||||||
|
const BrandWordmark: React.FC<Props> = ({
|
||||||
|
size = 'md',
|
||||||
|
className,
|
||||||
|
withMark = true,
|
||||||
|
withAdmin = false,
|
||||||
|
}) => {
|
||||||
|
const s = sizeMap[size];
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn('inline-flex items-center gap-2 text-foreground', className)}
|
||||||
|
data-testid="brand-wordmark"
|
||||||
|
aria-label={withAdmin ? 'CalenTIQ Admin' : 'CalenTIQ'}
|
||||||
|
>
|
||||||
|
{withMark && (
|
||||||
|
<svg
|
||||||
|
className={cn('shrink-0', s.mark)}
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
fill="none"
|
||||||
|
aria-hidden
|
||||||
|
>
|
||||||
|
<g
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="48"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M512 132 A380 380 0 1 0 512 892" />
|
||||||
|
<path d="M512 512 L377.77 434.50" />
|
||||||
|
<path d="M512 512 L698.20 404.50" />
|
||||||
|
</g>
|
||||||
|
<circle cx="512" cy="512" r="24" fill="currentColor" />
|
||||||
|
<circle cx="816.06" cy="207.94" r="18" fill="currentColor" />
|
||||||
|
<circle cx="884.39" cy="297.00" r="18" fill="currentColor" />
|
||||||
|
<circle cx="927.35" cy="400.71" r="18" fill="currentColor" />
|
||||||
|
<circle cx="942.00" cy="512.00" r="18" fill="currentColor" />
|
||||||
|
<circle cx="927.35" cy="623.29" r="18" fill="currentColor" />
|
||||||
|
<circle cx="884.39" cy="727.00" r="18" fill="currentColor" />
|
||||||
|
<circle cx="816.06" cy="816.06" r="18" fill="currentColor" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
<p className={cn('leading-none font-extrabold tracking-tight', s.text)}>
|
||||||
|
Calen<span className="text-primary">TIQ</span>
|
||||||
|
{withAdmin && (
|
||||||
|
<span className={cn('ml-1.5 font-semibold opacity-70', s.admin)}>Admin</span>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BrandWordmark;
|
||||||
@@ -38,6 +38,7 @@ import {
|
|||||||
} from '@/config/adminAccess';
|
} from '@/config/adminAccess';
|
||||||
import { notify } from '@/lib/notify';
|
import { notify } from '@/lib/notify';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import BrandWordmark from '@/components/BrandWordmark';
|
||||||
import { testid } from '@/lib/testid';
|
import { testid } from '@/lib/testid';
|
||||||
import { getTableDensity, toggleTableDensity, type TableDensity } from '@/lib/density';
|
import { getTableDensity, toggleTableDensity, type TableDensity } from '@/lib/density';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -251,11 +252,13 @@ const ControlCenterLayout: React.FC = () => {
|
|||||||
collapsed ? 'w-16' : 'w-60'
|
collapsed ? 'w-16' : 'w-60'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex h-14 items-center justify-between px-3 border-b border-white/10">
|
<div className="flex h-14 items-center justify-between gap-1 px-3 border-b border-white/10">
|
||||||
{!collapsed && (
|
{!collapsed && (
|
||||||
<span className="font-semibold text-sm truncate">
|
<BrandWordmark
|
||||||
{import.meta.env.VITE_APP_TITLE || t('common.appTitle')}
|
size="sm"
|
||||||
</span>
|
withAdmin
|
||||||
|
className="min-w-0 truncate text-sidebar-foreground [&_span.text-primary]:text-sidebar-foreground"
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"appTitle": "EventHub",
|
"appTitle": "CalenTIQ Admin",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"saving": "Saving…",
|
"saving": "Saving…",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"appTitle": "EventHub",
|
"appTitle": "CalenTIQ Admin",
|
||||||
"cancel": "Отмена",
|
"cancel": "Отмена",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"saving": "Сохранение…",
|
"saving": "Сохранение…",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import BrandWordmark from '@/components/BrandWordmark';
|
||||||
|
|
||||||
const LoginPage: React.FC = () => {
|
const LoginPage: React.FC = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -61,7 +62,7 @@ const LoginPage: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
<Card className="relative z-10 w-full max-w-md border-border/80 shadow-lg shadow-slate-900/5 backdrop-blur-sm">
|
<Card className="relative z-10 w-full max-w-md border-border/80 shadow-lg shadow-slate-900/5 backdrop-blur-sm">
|
||||||
<CardHeader className="space-y-3">
|
<CardHeader className="space-y-3">
|
||||||
<p className="text-3xl font-semibold tracking-tight text-foreground">EventHub</p>
|
<BrandWordmark size="md" withAdmin />
|
||||||
<div>
|
<div>
|
||||||
<CardTitle className="text-xl">{t('login.title')}</CardTitle>
|
<CardTitle className="text-xl">{t('login.title')}</CardTitle>
|
||||||
<CardDescription className="mt-1">{t('login.subtitle')}</CardDescription>
|
<CardDescription className="mt-1">{t('login.subtitle')}</CardDescription>
|
||||||
|
|||||||
Reference in New Issue
Block a user