feat: configurable icon_size (small/medium/large)
This commit is contained in:
parent
40ae190f32
commit
d36f16cea1
|
|
@ -1,15 +1,14 @@
|
||||||
site:
|
site:
|
||||||
name: "Soch Family"
|
name: "Soch Family"
|
||||||
title: " "
|
title: " "
|
||||||
#title: "Les applications de la famille"
|
|
||||||
logoUrl: "https://immich.socheleau.fr/api/assets/e010423f-ca95-45a4-a9ee-9bb1a5a48688/thumbnail?slug=us_6&size=preview&c=IhgKJYiHg4ZfhXh6h5aJp5eAdQlJ&edited=true"
|
logoUrl: "https://immich.socheleau.fr/api/assets/e010423f-ca95-45a4-a9ee-9bb1a5a48688/thumbnail?slug=us_6&size=preview&c=IhgKJYiHg4ZfhXh6h5aJp5eAdQlJ&edited=true"
|
||||||
#description: "Accès rapide à nos services"
|
|
||||||
showFooter: false
|
showFooter: false
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
mode: "dark"
|
mode: "dark"
|
||||||
accent: "#8b5cf6"
|
accent: "#8b5cf6"
|
||||||
background: "gradient"
|
background: "gradient"
|
||||||
|
icon_size: "large"
|
||||||
|
|
||||||
authentication:
|
authentication:
|
||||||
logout_url: "https://auth.socheleau.fr/logout"
|
logout_url: "https://auth.socheleau.fr/logout"
|
||||||
|
|
@ -19,66 +18,114 @@ authentication:
|
||||||
header_group: "Remote-Groups"
|
header_group: "Remote-Groups"
|
||||||
|
|
||||||
sections:
|
sections:
|
||||||
- id: "global"
|
- id: "soch-services"
|
||||||
title: "Global"
|
title: "Soch's services"
|
||||||
#description: "Services accessibles à toute la famille"
|
|
||||||
cardLayout: "horizontal"
|
cardLayout: "horizontal"
|
||||||
visible_by_group: "arr"
|
|
||||||
links:
|
links:
|
||||||
- id: "nextcloud"
|
- id: "authelia"
|
||||||
name: "Nextcloud"
|
name: "Authelia"
|
||||||
description: "Fichiers et documents"
|
description: "Authentification unique"
|
||||||
url: "https://cloud.example.com"
|
url: "https://auth.socheleau.fr"
|
||||||
icon: "Cloud"
|
icon: "Shield"
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg"
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg"
|
||||||
color: "#2daae1"
|
color: "#1a7f37"
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
visible_by_user: "jean"
|
|
||||||
|
|
||||||
- id: "jellyfin"
|
- id: "plex"
|
||||||
name: "Jellyfin"
|
name: "Plex"
|
||||||
description: "Films et séries"
|
description: "Films et séries"
|
||||||
url: "https://jellyfin.example.com"
|
url: "https://plex.socheleau.fr"
|
||||||
icon: "Film"
|
icon: "Film"
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyfin.svg"
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/plex.svg"
|
||||||
color: "#aa5cc3"
|
color: "#e5a00d"
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
|
|
||||||
- id: "navidrome"
|
- id: "bitwarden"
|
||||||
name: "Navidrome"
|
name: "Bitwarden"
|
||||||
description: "Musique"
|
description: "Mots de passe"
|
||||||
url: "https://music.example.com"
|
url: "https://bitwarden.socheleau.fr"
|
||||||
icon: "Music"
|
icon: "Shield"
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/navidrome.svg"
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/bitwarden.svg"
|
||||||
color: "#22c55e"
|
color: "#175ddc"
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
|
|
||||||
- id: "minecraft"
|
- id: "immich"
|
||||||
name: "Minecraft"
|
name: "Immich"
|
||||||
description: "Serveur de jeu"
|
description: "Photos et vidéos"
|
||||||
url: "https://minecraft.example.com"
|
url: "https://immich.socheleau.fr"
|
||||||
icon: "Gamepad2"
|
icon: "Image"
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/minecraft.svg"
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg"
|
||||||
color: "#f97316"
|
color: "#4250af"
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
|
|
||||||
- id: "custom"
|
- id: "freshrss"
|
||||||
title: "Liens personnalisés"
|
name: "FreshRSS"
|
||||||
|
description: "Flux RSS"
|
||||||
|
url: "https://rss.socheleau.fr"
|
||||||
|
icon: "Rss"
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/freshrss.svg"
|
||||||
|
color: "#0979b0"
|
||||||
|
openInNewTab: false
|
||||||
|
|
||||||
|
- id: "tandoor"
|
||||||
|
name: "Tandoor"
|
||||||
|
description: "Recettes de cuisine"
|
||||||
|
url: "https://tandoor.socheleau.fr"
|
||||||
|
icon: "ChefHat"
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/tandoor-recipes.svg"
|
||||||
|
color: "#dc7b2a"
|
||||||
|
openInNewTab: false
|
||||||
|
|
||||||
|
- id: "readeck"
|
||||||
|
name: "Readeck"
|
||||||
|
description: "Lecture différée"
|
||||||
|
url: "https://readeck.socheleau.fr"
|
||||||
|
icon: "BookOpen"
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/readeck.svg"
|
||||||
|
color: "#14b8a6"
|
||||||
|
openInNewTab: false
|
||||||
|
|
||||||
|
- id: "homes"
|
||||||
|
title: "Homes"
|
||||||
cardLayout: "horizontal"
|
cardLayout: "horizontal"
|
||||||
links:
|
links:
|
||||||
- id: "homeassistant"
|
- id: "arasu"
|
||||||
name: "Home Assistant"
|
name: "Arasu"
|
||||||
description: "Domotique"
|
description: "Domotique"
|
||||||
url: "https://ha.example.com"
|
url: "https://arasu.socheleau.fr"
|
||||||
icon: "House"
|
icon: "House"
|
||||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/home-assistant.svg"
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/home-assistant.svg"
|
||||||
color: "#22c55e"
|
color: "#22c55e"
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
|
|
||||||
- id: "router"
|
- id: "arr"
|
||||||
name: "Routeur"
|
title: "*arr"
|
||||||
description: "Administration réseau"
|
cardLayout: "horizontal"
|
||||||
url: "http://192.168.1.1"
|
visible_by_group: "arr"
|
||||||
icon: "Router"
|
links:
|
||||||
color: "#f59e0b"
|
- id: "radarr"
|
||||||
|
name: "Radarr"
|
||||||
|
description: "Gestion de films"
|
||||||
|
url: "https://radarr.arr.socheleau.fr"
|
||||||
|
icon: "Clapperboard"
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/radarr.svg"
|
||||||
|
color: "#e73a2e"
|
||||||
|
openInNewTab: false
|
||||||
|
|
||||||
|
- id: "sonarr"
|
||||||
|
name: "Sonarr"
|
||||||
|
description: "Gestion de séries"
|
||||||
|
url: "https://sonarr.arr.socheleau.fr"
|
||||||
|
icon: "Tv"
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/sonarr.svg"
|
||||||
|
color: "#35c5f4"
|
||||||
|
openInNewTab: false
|
||||||
|
|
||||||
|
- id: "prowlarr"
|
||||||
|
name: "Prowlarr"
|
||||||
|
description: "Indexeur de torrents"
|
||||||
|
url: "https://prowlarr.arr.socheleau.fr"
|
||||||
|
icon: "Search"
|
||||||
|
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/prowlarr.svg"
|
||||||
|
color: "#e65c21"
|
||||||
openInNewTab: false
|
openInNewTab: false
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
services:
|
services:
|
||||||
sochboard:
|
sochboard:
|
||||||
build: .
|
image: registry2.socheleau.fr/maxence/sochboard:latest
|
||||||
container_name: sochboard
|
container_name: sochboard
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export function Dashboard({ config, isAuthenticated = false, username = null, cu
|
||||||
/>
|
/>
|
||||||
<main className="flex flex-col gap-12">
|
<main className="flex flex-col gap-12">
|
||||||
{config.sections.map((section, index) => (
|
{config.sections.map((section, index) => (
|
||||||
<DashboardSection key={section.id} section={section} sectionIndex={index} currentUser={currentUser} userGroups={userGroups} />
|
<DashboardSection key={section.id} section={section} sectionIndex={index} currentUser={currentUser} userGroups={userGroups} iconSize={config.theme.icon_size} />
|
||||||
))}
|
))}
|
||||||
</main>
|
</main>
|
||||||
{config.site.showFooter !== false && (
|
{config.site.showFooter !== false && (
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
import { ServiceCard } from "./ServiceCard"
|
import { ServiceCard } from "./ServiceCard"
|
||||||
import { isVisible } from "@/lib/visibility"
|
import { isVisible } from "@/lib/visibility"
|
||||||
import type { SectionConfig } from "@/types/config"
|
import type { SectionConfig, IconSize } from "@/types/config"
|
||||||
|
|
||||||
interface DashboardSectionProps {
|
interface DashboardSectionProps {
|
||||||
section: SectionConfig
|
section: SectionConfig
|
||||||
sectionIndex?: number
|
sectionIndex?: number
|
||||||
currentUser?: string | null
|
currentUser?: string | null
|
||||||
userGroups?: string[]
|
userGroups?: string[]
|
||||||
|
iconSize?: IconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DashboardSection({ section, sectionIndex = 0, currentUser = null, userGroups = [] }: DashboardSectionProps) {
|
export function DashboardSection({ section, sectionIndex = 0, currentUser = null, userGroups = [], iconSize = "medium" }: DashboardSectionProps) {
|
||||||
if (!isVisible(section, currentUser, userGroups)) return null
|
if (!isVisible(section, currentUser, userGroups)) return null
|
||||||
|
|
||||||
const visibleLinks = section.links.filter((link) => isVisible(link, currentUser, userGroups))
|
const visibleLinks = section.links.filter((link) => isVisible(link, currentUser, userGroups))
|
||||||
|
|
@ -32,13 +33,14 @@ export function DashboardSection({ section, sectionIndex = 0, currentUser = null
|
||||||
<p className="mt-1 text-sm text-[var(--text-secondary)]">{section.description}</p>
|
<p className="mt-1 text-sm text-[var(--text-secondary)]">{section.description}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 sm:gap-4">
|
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 sm:gap-4">
|
||||||
{visibleLinks.map((link, index) => (
|
{visibleLinks.map((link, index) => (
|
||||||
<ServiceCard
|
<ServiceCard
|
||||||
key={link.id}
|
key={link.id}
|
||||||
link={link}
|
link={link}
|
||||||
index={index}
|
index={index}
|
||||||
layout={section.cardLayout}
|
layout={section.cardLayout}
|
||||||
|
iconSize={iconSize}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,26 @@
|
||||||
import { IconRenderer } from "./IconRenderer"
|
import { IconRenderer } from "./IconRenderer"
|
||||||
import type { LinkConfig } from "@/types/config"
|
import type { LinkConfig, IconSize } from "@/types/config"
|
||||||
|
|
||||||
interface ServiceCardProps {
|
interface ServiceCardProps {
|
||||||
link: LinkConfig
|
link: LinkConfig
|
||||||
index?: number
|
index?: number
|
||||||
layout?: "horizontal" | "vertical"
|
layout?: "horizontal" | "vertical"
|
||||||
|
iconSize?: IconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceCardProps) {
|
const sizeMap: Record<IconSize, { hCont: number; hImg: string; hIcon: number; vCont: number; vImg: string; vIcon: number }> = {
|
||||||
|
small: { hCont: 10, hImg: "size-5", hIcon: 18, vCont: 12, vImg: "size-6", vIcon: 22 },
|
||||||
|
medium: { hCont: 12, hImg: "size-7", hIcon: 24, vCont: 14, vImg: "size-8", vIcon: 30 },
|
||||||
|
large: { hCont: 12, hImg: "size-8", hIcon: 28, vCont: 14, vImg: "size-9", vIcon: 34 },
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ServiceCard({ link, index = 0, layout = "horizontal", iconSize = "medium" }: ServiceCardProps) {
|
||||||
const linkProps = link.openInNewTab
|
const linkProps = link.openInNewTab
|
||||||
? { target: "_blank", rel: "noopener noreferrer" }
|
? { target: "_blank", rel: "noopener noreferrer" }
|
||||||
: {}
|
: {}
|
||||||
|
|
||||||
const isHorizontal = layout === "horizontal"
|
const isHorizontal = layout === "horizontal"
|
||||||
|
const s = sizeMap[iconSize]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
|
|
@ -24,16 +32,18 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
||||||
{isHorizontal ? (
|
{isHorizontal ? (
|
||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
className="flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-lg"
|
className="flex shrink-0 items-center justify-center overflow-hidden rounded-lg"
|
||||||
style={{
|
style={{
|
||||||
|
width: s.hCont * 4,
|
||||||
|
height: s.hCont * 4,
|
||||||
backgroundColor: `${link.color || "#8b5cf6"}20`,
|
backgroundColor: `${link.color || "#8b5cf6"}20`,
|
||||||
color: link.color || "#8b5cf6",
|
color: link.color || "#8b5cf6",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{link.iconUrl ? (
|
{link.iconUrl ? (
|
||||||
<img src={link.iconUrl} alt="" className="size-5 object-contain" />
|
<img src={link.iconUrl} alt="" className={`${s.hImg} object-contain`} />
|
||||||
) : (
|
) : (
|
||||||
<IconRenderer name={link.icon} size={18} />
|
<IconRenderer name={link.icon} size={s.hIcon} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex min-w-0 flex-1 flex-col">
|
<div className="flex min-w-0 flex-1 flex-col">
|
||||||
|
|
@ -73,16 +83,18 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
||||||
<>
|
<>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div
|
<div
|
||||||
className="flex size-12 items-center justify-center overflow-hidden rounded-xl"
|
className="flex items-center justify-center overflow-hidden rounded-xl"
|
||||||
style={{
|
style={{
|
||||||
|
width: s.vCont * 4,
|
||||||
|
height: s.vCont * 4,
|
||||||
backgroundColor: `${link.color || "#8b5cf6"}20`,
|
backgroundColor: `${link.color || "#8b5cf6"}20`,
|
||||||
color: link.color || "#8b5cf6",
|
color: link.color || "#8b5cf6",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{link.iconUrl ? (
|
{link.iconUrl ? (
|
||||||
<img src={link.iconUrl} alt="" className="size-6 object-contain" />
|
<img src={link.iconUrl} alt="" className={`${s.vImg} object-contain`} />
|
||||||
) : (
|
) : (
|
||||||
<IconRenderer name={link.icon} size={22} />
|
<IconRenderer name={link.icon} size={s.vIcon} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{link.openInNewTab && (
|
{link.openInNewTab && (
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ export const themeSchema = z.object({
|
||||||
.regex(hexColorRegex, "Invalid accent color")
|
.regex(hexColorRegex, "Invalid accent color")
|
||||||
.default("#8b5cf6"),
|
.default("#8b5cf6"),
|
||||||
background: z.enum(["gradient", "solid"]).default("gradient"),
|
background: z.enum(["gradient", "solid"]).default("gradient"),
|
||||||
|
icon_size: z.enum(["small", "medium", "large"]).optional().default("medium"),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const siteSchema = z.object({
|
export const siteSchema = z.object({
|
||||||
|
|
@ -57,6 +58,7 @@ export const dashboardConfigSchema = z.object({
|
||||||
mode: "dark",
|
mode: "dark",
|
||||||
accent: "#8b5cf6",
|
accent: "#8b5cf6",
|
||||||
background: "gradient",
|
background: "gradient",
|
||||||
|
icon_size: "medium",
|
||||||
}),
|
}),
|
||||||
authentication: authenticationSchema.optional(),
|
authentication: authenticationSchema.optional(),
|
||||||
sections: z.array(sectionSchema).default([]),
|
sections: z.array(sectionSchema).default([]),
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,13 @@ export interface SiteConfig {
|
||||||
showFooter?: boolean
|
showFooter?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type IconSize = "small" | "medium" | "large"
|
||||||
|
|
||||||
export interface ThemeConfig {
|
export interface ThemeConfig {
|
||||||
mode: "dark" | "light"
|
mode: "dark" | "light"
|
||||||
accent: string
|
accent: string
|
||||||
background: "gradient" | "solid"
|
background: "gradient" | "solid"
|
||||||
|
icon_size?: IconSize
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LinkConfig {
|
export interface LinkConfig {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue