feat: configurable icon_size (small/medium/large)
This commit is contained in:
parent
40ae190f32
commit
d36f16cea1
|
|
@ -1,15 +1,14 @@
|
|||
site:
|
||||
name: "Soch Family"
|
||||
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"
|
||||
#description: "Accès rapide à nos services"
|
||||
showFooter: false
|
||||
|
||||
theme:
|
||||
mode: "dark"
|
||||
accent: "#8b5cf6"
|
||||
background: "gradient"
|
||||
icon_size: "large"
|
||||
|
||||
authentication:
|
||||
logout_url: "https://auth.socheleau.fr/logout"
|
||||
|
|
@ -19,66 +18,114 @@ authentication:
|
|||
header_group: "Remote-Groups"
|
||||
|
||||
sections:
|
||||
- id: "global"
|
||||
title: "Global"
|
||||
#description: "Services accessibles à toute la famille"
|
||||
- id: "soch-services"
|
||||
title: "Soch's services"
|
||||
cardLayout: "horizontal"
|
||||
visible_by_group: "arr"
|
||||
links:
|
||||
- id: "nextcloud"
|
||||
name: "Nextcloud"
|
||||
description: "Fichiers et documents"
|
||||
url: "https://cloud.example.com"
|
||||
icon: "Cloud"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/nextcloud.svg"
|
||||
color: "#2daae1"
|
||||
- id: "authelia"
|
||||
name: "Authelia"
|
||||
description: "Authentification unique"
|
||||
url: "https://auth.socheleau.fr"
|
||||
icon: "Shield"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/authelia.svg"
|
||||
color: "#1a7f37"
|
||||
openInNewTab: false
|
||||
visible_by_user: "jean"
|
||||
|
||||
- id: "jellyfin"
|
||||
name: "Jellyfin"
|
||||
- id: "plex"
|
||||
name: "Plex"
|
||||
description: "Films et séries"
|
||||
url: "https://jellyfin.example.com"
|
||||
url: "https://plex.socheleau.fr"
|
||||
icon: "Film"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyfin.svg"
|
||||
color: "#aa5cc3"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/plex.svg"
|
||||
color: "#e5a00d"
|
||||
openInNewTab: false
|
||||
|
||||
- id: "navidrome"
|
||||
name: "Navidrome"
|
||||
description: "Musique"
|
||||
url: "https://music.example.com"
|
||||
icon: "Music"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/navidrome.svg"
|
||||
color: "#22c55e"
|
||||
- id: "bitwarden"
|
||||
name: "Bitwarden"
|
||||
description: "Mots de passe"
|
||||
url: "https://bitwarden.socheleau.fr"
|
||||
icon: "Shield"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/bitwarden.svg"
|
||||
color: "#175ddc"
|
||||
openInNewTab: false
|
||||
|
||||
- id: "minecraft"
|
||||
name: "Minecraft"
|
||||
description: "Serveur de jeu"
|
||||
url: "https://minecraft.example.com"
|
||||
icon: "Gamepad2"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/minecraft.svg"
|
||||
color: "#f97316"
|
||||
- id: "immich"
|
||||
name: "Immich"
|
||||
description: "Photos et vidéos"
|
||||
url: "https://immich.socheleau.fr"
|
||||
icon: "Image"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/immich.svg"
|
||||
color: "#4250af"
|
||||
openInNewTab: false
|
||||
|
||||
- id: "custom"
|
||||
title: "Liens personnalisés"
|
||||
- id: "freshrss"
|
||||
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"
|
||||
links:
|
||||
- id: "homeassistant"
|
||||
name: "Home Assistant"
|
||||
- id: "arasu"
|
||||
name: "Arasu"
|
||||
description: "Domotique"
|
||||
url: "https://ha.example.com"
|
||||
url: "https://arasu.socheleau.fr"
|
||||
icon: "House"
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/home-assistant.svg"
|
||||
color: "#22c55e"
|
||||
openInNewTab: false
|
||||
|
||||
- id: "router"
|
||||
name: "Routeur"
|
||||
description: "Administration réseau"
|
||||
url: "http://192.168.1.1"
|
||||
icon: "Router"
|
||||
color: "#f59e0b"
|
||||
- id: "arr"
|
||||
title: "*arr"
|
||||
cardLayout: "horizontal"
|
||||
visible_by_group: "arr"
|
||||
links:
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
sochboard:
|
||||
build: .
|
||||
image: registry2.socheleau.fr/maxence/sochboard:latest
|
||||
container_name: sochboard
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export function Dashboard({ config, isAuthenticated = false, username = null, cu
|
|||
/>
|
||||
<main className="flex flex-col gap-12">
|
||||
{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>
|
||||
{config.site.showFooter !== false && (
|
||||
|
|
@ -32,4 +32,4 @@ export function Dashboard({ config, isAuthenticated = false, username = null, cu
|
|||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
import { ServiceCard } from "./ServiceCard"
|
||||
import { isVisible } from "@/lib/visibility"
|
||||
import type { SectionConfig } from "@/types/config"
|
||||
import type { SectionConfig, IconSize } from "@/types/config"
|
||||
|
||||
interface DashboardSectionProps {
|
||||
section: SectionConfig
|
||||
sectionIndex?: number
|
||||
currentUser?: string | null
|
||||
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
|
||||
|
||||
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>
|
||||
)}
|
||||
</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) => (
|
||||
<ServiceCard
|
||||
key={link.id}
|
||||
link={link}
|
||||
index={index}
|
||||
layout={section.cardLayout}
|
||||
iconSize={iconSize}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,26 @@
|
|||
import { IconRenderer } from "./IconRenderer"
|
||||
import type { LinkConfig } from "@/types/config"
|
||||
import type { LinkConfig, IconSize } from "@/types/config"
|
||||
|
||||
interface ServiceCardProps {
|
||||
link: LinkConfig
|
||||
index?: number
|
||||
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
|
||||
? { target: "_blank", rel: "noopener noreferrer" }
|
||||
: {}
|
||||
|
||||
const isHorizontal = layout === "horizontal"
|
||||
const s = sizeMap[iconSize]
|
||||
|
||||
return (
|
||||
<a
|
||||
|
|
@ -24,16 +32,18 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
|||
{isHorizontal ? (
|
||||
<>
|
||||
<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={{
|
||||
width: s.hCont * 4,
|
||||
height: s.hCont * 4,
|
||||
backgroundColor: `${link.color || "#8b5cf6"}20`,
|
||||
color: link.color || "#8b5cf6",
|
||||
}}
|
||||
>
|
||||
{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 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 size-12 items-center justify-center overflow-hidden rounded-xl"
|
||||
className="flex items-center justify-center overflow-hidden rounded-xl"
|
||||
style={{
|
||||
width: s.vCont * 4,
|
||||
height: s.vCont * 4,
|
||||
backgroundColor: `${link.color || "#8b5cf6"}20`,
|
||||
color: link.color || "#8b5cf6",
|
||||
}}
|
||||
>
|
||||
{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>
|
||||
{link.openInNewTab && (
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export const themeSchema = z.object({
|
|||
.regex(hexColorRegex, "Invalid accent color")
|
||||
.default("#8b5cf6"),
|
||||
background: z.enum(["gradient", "solid"]).default("gradient"),
|
||||
icon_size: z.enum(["small", "medium", "large"]).optional().default("medium"),
|
||||
})
|
||||
|
||||
export const siteSchema = z.object({
|
||||
|
|
@ -57,6 +58,7 @@ export const dashboardConfigSchema = z.object({
|
|||
mode: "dark",
|
||||
accent: "#8b5cf6",
|
||||
background: "gradient",
|
||||
icon_size: "medium",
|
||||
}),
|
||||
authentication: authenticationSchema.optional(),
|
||||
sections: z.array(sectionSchema).default([]),
|
||||
|
|
|
|||
|
|
@ -7,10 +7,13 @@ export interface SiteConfig {
|
|||
showFooter?: boolean
|
||||
}
|
||||
|
||||
export type IconSize = "small" | "medium" | "large"
|
||||
|
||||
export interface ThemeConfig {
|
||||
mode: "dark" | "light"
|
||||
accent: string
|
||||
background: "gradient" | "solid"
|
||||
icon_size?: IconSize
|
||||
}
|
||||
|
||||
export interface LinkConfig {
|
||||
|
|
|
|||
Loading…
Reference in a new issue