web: logo on the login page, icon as favicon and in the headers
public/logo.png is the source; icon-192.png (the mark, cropped and padded on the logo's background) and logo-512.png (login card) are cut from it with ffmpeg so pages don't load the 1.2 MB original. Brand (icon + name) replaces the plain app name in the admin and My Phone headers.
This commit is contained in:
1 parent
863a9d121c
commit
964a3c0b25
8 files changed
+25
-8
No files matched your search
@@ -4,6 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>SelfPhoneNG</title>
|
||||
<link rel="icon" type="image/png" href="/icon-192.png" />
|
||||
<link rel="apple-touch-icon" href="/icon-192.png" />
|
||||
<script>
|
||||
// Apply the theme before first paint so dark mode doesn't flash.
|
||||
try {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 213 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
// Small form building blocks shared by every page.
|
||||
import { useId, type ComponentProps, type ReactNode } from 'react';
|
||||
import { APP_NAME } from '@selfphoneng/shared';
|
||||
|
||||
const focus =
|
||||
'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-500';
|
||||
@@ -13,6 +14,16 @@ const variants = {
|
||||
ghost: 'hover:bg-zinc-100 dark:hover:bg-zinc-800',
|
||||
};
|
||||
|
||||
/** The icon (public/icon-192.png, cut from public/logo.png) and the product name. */
|
||||
export function Brand({ className = '' }: { className?: string }) {
|
||||
return (
|
||||
<span className={`inline-flex items-center gap-2 font-semibold ${className}`}>
|
||||
<img src="/icon-192.png" alt="" className="size-7 rounded-md" />
|
||||
{APP_NAME}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function Button({
|
||||
variant = 'secondary',
|
||||
className = '',
|
||||
|
||||
@@ -13,7 +13,7 @@ export function AuthCard({ title, children }: { title: string; children: React.R
|
||||
return (
|
||||
<main className="flex min-h-dvh items-center justify-center p-4">
|
||||
<div className="w-full max-w-sm rounded-lg border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p className="mb-1 text-sm font-semibold text-sky-600">{APP_NAME}</p>
|
||||
<img src="/logo-512.png" alt={APP_NAME} className="mx-auto mb-4 size-32 rounded-xl" />
|
||||
<h1 className="mb-5 text-xl font-semibold">{title}</h1>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Navigate, NavLink, Outlet, useNavigate } from 'react-router';
|
||||
import { APP_NAME, type AuthUser } from '@selfphoneng/shared';
|
||||
import type { AuthUser } from '@selfphoneng/shared';
|
||||
import { authApi, systemApi } from '../../api/client.ts';
|
||||
import { ThemeToggle } from '../../components/ThemeToggle.tsx';
|
||||
import { Button } from '../../components/ui.tsx';
|
||||
import { Brand, Button } from '../../components/ui.tsx';
|
||||
import { subscribeLive, useLive } from '../../live/store.ts';
|
||||
|
||||
export const ADMIN_PAGES = [
|
||||
@@ -75,7 +75,9 @@ export function AdminLayout({ user }: { user: AuthUser }) {
|
||||
return (
|
||||
<div className="flex min-h-dvh">
|
||||
<aside className="sticky top-0 hidden h-dvh w-60 flex-col border-r border-zinc-200 bg-zinc-50 md:flex dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<p className="px-4 py-4 text-lg font-semibold">{APP_NAME}</p>
|
||||
<p className="px-4 py-4 text-lg">
|
||||
<Brand />
|
||||
</p>
|
||||
{links}
|
||||
</aside>
|
||||
|
||||
@@ -91,7 +93,7 @@ export function AdminLayout({ user }: { user: AuthUser }) {
|
||||
>
|
||||
☰
|
||||
</Button>
|
||||
<span className="font-semibold md:hidden">{APP_NAME}</span>
|
||||
<Brand className="md:hidden" />
|
||||
<div className="ml-auto flex items-center gap-1 whitespace-nowrap sm:gap-2">
|
||||
<span className="hidden text-sm text-zinc-600 sm:inline dark:text-zinc-400">
|
||||
{user.displayName}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useEffect } from 'react';
|
||||
import { Link, NavLink, Route, Routes, useNavigate } from 'react-router';
|
||||
import { APP_NAME, type AuthUser } from '@selfphoneng/shared';
|
||||
import type { AuthUser } from '@selfphoneng/shared';
|
||||
import { authApi, meApi } from '../../api/client.ts';
|
||||
import { ThemeToggle } from '../../components/ThemeToggle.tsx';
|
||||
import { Alert, Button } from '../../components/ui.tsx';
|
||||
import { Alert, Brand, Button } from '../../components/ui.tsx';
|
||||
import { subscribeLive, useLive } from '../../live/store.ts';
|
||||
import { SoftphoneDock } from '../../softphone/SoftphoneDock.tsx';
|
||||
import { PresencePanel } from '../../softphone/PresencePanel.tsx';
|
||||
@@ -40,7 +40,9 @@ export function MePage({ user }: { user: AuthUser }) {
|
||||
return (
|
||||
<main className="mx-auto flex min-h-dvh max-w-6xl flex-col gap-6 p-4 sm:p-8">
|
||||
<header className="flex flex-wrap items-center justify-between gap-2">
|
||||
<h1 className="text-xl font-semibold">{APP_NAME}</h1>
|
||||
<h1 className="text-xl">
|
||||
<Brand />
|
||||
</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
{user.role === 'admin' && (
|
||||
<Link className={`${link} text-sm`} to="/admin">
|
||||
|
||||
Reference in new issue
Block a user