Merge branch 'dev' into feat/docker-logs

This commit is contained in:
ajnart authored and GitHub committed 2026-01-15 21:19:39 +01:00
commit fb3da7e997
86 files changed
+1295 -947

No files matched your search

+1
View File
@@ -22,6 +22,7 @@ export const apiKeysRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
},
},
+3
View File
@@ -155,6 +155,7 @@ export const boardRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
},
userPermissions: {
@@ -1195,6 +1196,7 @@ export const boardRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
},
},
@@ -1537,6 +1539,7 @@ const getFullBoardWithWhereAsync = async (db: Database, where: SQL<unknown>, use
id: true,
name: true,
image: true,
email: true,
},
},
sections: {
@@ -476,6 +476,7 @@ export const integrationRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
},
},
@@ -39,6 +39,7 @@ export const mediaRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
},
},
+2 -2
View File
@@ -1220,11 +1220,11 @@ describe("getBoardPermissions should return board permissions", () => {
expect(result.users).toEqual(
expect.arrayContaining([
{
user: { id: user1, name: null, image: null },
user: { id: user1, name: null, image: null, email: null },
permission: "view",
},
{
user: { id: user2, name: null, image: null },
user: { id: user2, name: null, image: null, email: null },
permission: "modify",
},
]),
+5 -2
View File
@@ -174,7 +174,7 @@ export const userRouter = createTRPCRouter({
// Is protected because also used in board access / integration access forms
selectable: protectedProcedure
.input(z.object({ excludeExternalProviders: z.boolean().default(false) }).optional())
.output(z.array(selectUserSchema.pick({ id: true, name: true, image: true })))
.output(z.array(selectUserSchema.pick({ id: true, name: true, image: true, email: true })))
.meta({ openapi: { method: "GET", path: "/api/users/selectable", tags: ["users"], protect: true } })
.query(({ ctx, input }) => {
return ctx.db.query.users.findMany({
@@ -182,6 +182,7 @@ export const userRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
where: input?.excludeExternalProviders ? eq(users.provider, "credentials") : undefined,
});
@@ -194,7 +195,7 @@ export const userRouter = createTRPCRouter({
limit: z.number().min(1).max(100).default(10),
}),
)
.output(z.array(selectUserSchema.pick({ id: true, name: true, image: true })))
.output(z.array(selectUserSchema.pick({ id: true, name: true, image: true, email: true })))
.meta({ openapi: { method: "POST", path: "/api/users/search", tags: ["users"], protect: true } })
.query(async ({ input, ctx }) => {
const dbUsers = await ctx.db.query.users.findMany({
@@ -202,6 +203,7 @@ export const userRouter = createTRPCRouter({
id: true,
name: true,
image: true,
email: true,
},
where: like(users.name, `%${input.query}%`),
limit: input.limit,
@@ -210,6 +212,7 @@ export const userRouter = createTRPCRouter({
id: user.id,
name: user.name ?? "",
image: user.image,
email: user.email,
}));
}),
getById: protectedProcedure
+1 -1
View File
@@ -33,7 +33,7 @@
"@homarr/validation": "workspace:^0.1.0",
"bcrypt": "^6.0.0",
"cookies": "^0.9.1",
"ldapts": "8.1.0",
"ldapts": "8.1.3",
"next": "16.1.1",
"next-auth": "5.0.0-beta.30",
"react": "19.2.3",
+1 -1
View File
@@ -33,7 +33,7 @@
"octokit": "^5.0.5",
"react": "19.2.3",
"react-dom": "19.2.3",
"undici": "7.16.0",
"undici": "7.18.2",
"zod": "^4.2.1",
"zod-validation-error": "^5.0.0"
},
+1 -1
View File
@@ -41,7 +41,7 @@
"better-sqlite3": "^12.5.0",
"dns-caching": "^0.2.9",
"drizzle-orm": "^0.45.1",
"ioredis": "5.8.2",
"ioredis": "5.9.1",
"mysql2": "3.16.0",
"pg": "^8.16.3",
"superjson": "2.2.6",
+1 -1
View File
@@ -41,7 +41,7 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/node-cron": "^3.0.11",
"@types/react": "19.2.7",
"@types/react": "19.2.8",
"eslint": "^9.39.2",
"typescript": "^5.9.3"
}
+1 -1
View File
@@ -47,7 +47,7 @@
"@homarr/core": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@mantine/core": "^8.3.12",
"@paralleldrive/cuid2": "^3.1.0",
"@testcontainers/mysql": "^11.11.0",
"@testcontainers/postgresql": "^11.11.0",
+1 -1
View File
@@ -26,7 +26,7 @@
"@homarr/common": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/form": "^8.3.10",
"@mantine/form": "^8.3.12",
"mantine-form-zod-resolver": "^1.3.0",
"zod": "^4.2.1"
},
+1 -1
View File
@@ -30,7 +30,7 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@mantine/core": "^8.3.12",
"react": "19.2.3",
"zod": "^4.2.1"
},
+1 -1
View File
@@ -46,7 +46,7 @@
"octokit": "^5.0.5",
"proxmox-api": "1.1.1",
"tsdav": "^2.1.6",
"undici": "7.16.0",
"undici": "7.18.2",
"xml2js": "^0.6.2",
"zod": "^4.2.1"
},
+2 -2
View File
@@ -33,8 +33,8 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@tabler/icons-react": "^3.36.0",
"@mantine/core": "^8.3.12",
"@tabler/icons-react": "^3.36.1",
"dayjs": "^1.11.19",
"next": "16.1.1",
"react": "19.2.3",
+2 -2
View File
@@ -24,8 +24,8 @@
"dependencies": {
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@mantine/core": "^8.3.12",
"@mantine/hooks": "^8.3.12",
"react": "19.2.3"
},
"devDependencies": {
+2 -2
View File
@@ -24,8 +24,8 @@
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/ui": "workspace:^0.1.0",
"@mantine/notifications": "^8.3.10",
"@tabler/icons-react": "^3.36.0"
"@mantine/notifications": "^8.3.12",
"@tabler/icons-react": "^3.36.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
+2 -2
View File
@@ -37,8 +37,8 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@mantine/core": "^8.3.12",
"@mantine/hooks": "^8.3.12",
"adm-zip": "0.5.16",
"next": "16.1.1",
"react": "19.2.3",
+1 -1
View File
@@ -26,7 +26,7 @@
"@homarr/core": "workspace:^",
"@homarr/db": "workspace:^",
"@homarr/definitions": "workspace:^",
"ioredis": "5.8.2",
"ioredis": "5.9.1",
"superjson": "2.2.6"
},
"devDependencies": {
+1 -1
View File
@@ -33,7 +33,7 @@
"dayjs": "^1.11.19",
"octokit": "^5.0.5",
"superjson": "2.2.6",
"undici": "7.16.0",
"undici": "7.18.2",
"zod": "^4.2.1"
},
"devDependencies": {
@@ -57,12 +57,6 @@ export const createRequestIntegrationJobHandler = <
reduceWidgetOptionsWithDefaultValues(
itemForIntegration.kind,
{
defaultSearchEngineId: serverSettings.search.defaultSearchEngineId,
openSearchInNewTab: true,
firstDayOfWeek: 1,
homeBoardId: serverSettings.board.homeBoardId,
mobileHomeBoardId: serverSettings.board.mobileHomeBoardId,
pingIconsEnabled: true,
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
},
+4
View File
@@ -5,6 +5,7 @@ export const defaultServerSettingsKeys = [
"analytics",
"crawlingAndIndexing",
"board",
"user",
"appearance",
"culture",
"search",
@@ -31,6 +32,9 @@ export const defaultServerSettings = {
enableStatusByDefault: true,
forceDisableStatus: false,
},
user: {
enableGravatar: true,
},
appearance: {
defaultColorScheme: "light" as ColorScheme,
},
+1 -2
View File
@@ -23,10 +23,9 @@
},
"prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/api": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@mantine/dates": "^8.3.10",
"@mantine/dates": "^8.3.12",
"next": "16.1.1",
"react": "19.2.3",
"react-dom": "19.2.3"
+4 -1
View File
@@ -10,7 +10,8 @@ export type SettingsContextProps = Pick<
| "openSearchInNewTab"
| "pingIconsEnabled"
> &
Pick<ServerSettings["board"], "enableStatusByDefault" | "forceDisableStatus">;
Pick<ServerSettings["board"], "enableStatusByDefault" | "forceDisableStatus"> &
Pick<ServerSettings["user"], "enableGravatar">;
export interface PublicServerSettings {
search: Pick<ServerSettings["search"], "defaultSearchEngineId">;
@@ -18,6 +19,7 @@ export interface PublicServerSettings {
ServerSettings["board"],
"homeBoardId" | "mobileHomeBoardId" | "enableStatusByDefault" | "forceDisableStatus"
>;
user: Pick<ServerSettings["user"], "enableGravatar">;
}
export type UserSettings = Pick<
@@ -45,4 +47,5 @@ export const createSettings = ({
pingIconsEnabled: user?.pingIconsEnabled ?? false,
enableStatusByDefault: serverSettings.board.enableStatusByDefault,
forceDisableStatus: serverSettings.board.forceDisableStatus,
enableGravatar: serverSettings.user.enableGravatar,
});
+4 -4
View File
@@ -33,10 +33,10 @@
"@homarr/settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@mantine/spotlight": "^8.3.10",
"@tabler/icons-react": "^3.36.0",
"@mantine/core": "^8.3.12",
"@mantine/hooks": "^8.3.12",
"@mantine/spotlight": "^8.3.12",
"@tabler/icons-react": "^3.36.1",
"jotai": "^2.16.1",
"next": "16.1.1",
"react": "19.2.3",
@@ -11,7 +11,7 @@ import { interaction } from "../../lib/interaction";
// This has to be type so it can be interpreted as Record<string, unknown>.
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type User = { id: string; name: string; image: string | null };
type User = { id: string; name: string; image: string | null; email: string | null };
const userChildrenOptions = createChildrenOptions<User>({
useActions: () => [
+1 -1
View File
@@ -33,7 +33,7 @@
"deepmerge": "4.3.1",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.1.1",
"next-intl": "4.6.1",
"next-intl": "4.7.0",
"react": "19.2.3",
"react-dom": "19.2.3"
},
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "搜索",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "crwdns3822:0crwdne3822:0",
"enableGravatar": {
"label": "crwdns3824:0crwdne3824:0",
"description": "crwdns3826:0crwdne3826:0"
}
},
"search": {
"title": "crwdns2550:0crwdne2550:0",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Søg",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Suche",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Suche",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "Users",
"enableGravatar": {
"label": "Enable Gravatar",
"description": "Falls back to user avatars from Libravatar/Gravatar when no custom avatar is set and an email is configured"
}
},
"search": {
"title": "Search",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Buscar",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+8 -1
View File
@@ -2,7 +2,7 @@
"init": {
"step": {
"start": {
"title": "Bienvenue dans Homarr",
"title": "Bienvenue sur Homarr",
"subtitle": "Commençons par configurer votre instance Homarr.",
"description": "Pour commencer, veuillez selectionner comment vous souhaitez configurer votre instance Homarr.",
"action": {
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "Utilisateurs",
"enableGravatar": {
"label": "Activer Gravatar",
"description": "Revient aux avatars des utilisateurs de Libravatar/Gravatar quand aucun avatar personnalisé n'est défini et qu'un e-mail est configuré"
}
},
"search": {
"title": "Rechercher",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "חיפוש",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "Utenti",
"enableGravatar": {
"label": "Abilita Gravatar",
"description": "Torna agli avatar degli utenti da Libravatar/Gravatar quando non è impostato alcun avatar personalizzato e viene configurata un'email"
}
},
"search": {
"title": "Cerca",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "検索",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+8 -1
View File
@@ -748,7 +748,7 @@
"statusCode": {
"title": "Antwoord fout",
"description": "Onverwachte {statusCode} ({reason}) reactie van <url></url>. Controleer of de URL wijst naar de basis-URL van de integratie.",
"otherDescription": "",
"otherDescription": "Onverwachte statuscode {statusCode} ontvangen van <url></url>. Controleer of de URL verwijst naar de basis-URL van de integratie.",
"reason": {
"badRequest": "Onjuist verzoek",
"notFound": "Niet gevonden",
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Zoeken",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "Brukere",
"enableGravatar": {
"label": "Aktiver Gravatar",
"description": "Bruker avatarer fra Libravatar/Gravatar som reserve når ingen egendefinert avatar er angitt og en e-post er konfigurert"
}
},
"search": {
"title": "Søk",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Szukaj",
"defaultSearchEngine": {
+66 -59
View File
@@ -342,7 +342,7 @@
},
"full-all": {
"label": "Acesso completo a aplicativos",
"description": ""
"description": "Permitir que membros gerenciem, usem e excluam qualquer aplicativo"
}
}
},
@@ -350,104 +350,104 @@
"title": "Placas",
"item": {
"create": {
"label": "",
"description": ""
"label": "Criar painel",
"description": "Permitir que membros criem painéis"
},
"view-all": {
"label": "",
"description": ""
"label": "Ver todos os painéis",
"description": "Permitir que membros visualizem todos os painéis"
},
"modify-all": {
"label": "",
"description": ""
"label": "Modificar todos os painéis",
"description": "Permitir que membros modifiquem todos os painéis (Não inclui o controle de acesso e a zona de perigo)"
},
"full-all": {
"label": "",
"description": ""
"label": "Acesso total ao painel",
"description": "Permitir que membros vejam, modifiquem e deletem todos os painéis (Incluindo o controle de acesso e a zona de perigo)"
}
}
},
"integration": {
"title": "",
"title": "Integrações",
"item": {
"create": {
"label": "",
"description": ""
"label": "Criar integração",
"description": "Permitir que membros criem integrações"
},
"use-all": {
"label": "",
"description": ""
"label": "Usar todas as integrações",
"description": "Permitir que membros adicionem qualquer integração aos seus painéis"
},
"interact-all": {
"label": "",
"description": ""
"label": "Interagir com qualquer integração",
"description": "Permitir que membros interajam com qualquer integração"
},
"full-all": {
"label": "",
"description": ""
"label": "Acesso total às integrações",
"description": "Permitir que membros gerenciem, usem e interajam com qualquer integração"
}
}
},
"media": {
"title": "",
"title": "Mídias",
"item": {
"upload": {
"label": "",
"description": ""
"label": "Adicionar Mídia",
"description": "Permitir que membros adicionem mídias"
},
"view-all": {
"label": "",
"description": ""
"label": "Visualizar todas as mídias",
"description": "Permitir que membros visualizem todas as mídias"
},
"full-all": {
"label": "",
"description": ""
"label": "Acesso total às mídias",
"description": "Permitir que membros gerenciem e deletem qualquer mídia"
}
}
},
"other": {
"title": "",
"title": "Outras",
"item": {
"view-logs": {
"label": "",
"label": "Visualizar registros",
"description": "Permitir que os membros visualizem os registros"
}
}
},
"search-engine": {
"title": "",
"title": "Ferramentas de busca",
"item": {
"create": {
"label": "",
"label": "Criar ferramenta de busca",
"description": "Permitir que os membros criem mecanismos de busca"
},
"modify-all": {
"label": "",
"description": ""
"label": "Modificar todas as ferramentas de busca",
"description": "Permitir que membros modifiquem todas as ferramentas de busca"
},
"full-all": {
"label": "",
"description": ""
"label": "Acesso total às ferramentas de busca",
"description": "Permitir que membros modifiquem e deletem qualquer ferramenta de busca"
}
}
}
},
"memberNotice": {
"mixed": "",
"external": ""
"mixed": "Alguns membros são de provedores externos e não podem ser gerenciados aqui",
"external": "Todos os membros são de provedores externos e não podem ser gerenciados aqui"
},
"reservedNotice": {
"message": ""
"message": "Esse grupo é reservado para uso do sistema e restringe algumas ações. <checkoutDocs></checkoutDocs>"
},
"action": {
"create": {
"label": "",
"label": "Novo grupo",
"notification": {
"success": {
"message": ""
"message": "O grupo foi criado com sucesso"
},
"error": {
"message": ""
"message": "Não foi possível criar o grupo"
}
}
},
@@ -457,64 +457,64 @@
"confirm": "Tem certeza de que deseja transferir a propriedade do grupo {name} para {username}?",
"notification": {
"success": {
"message": ""
"message": "Grupo {group} transferido para {user} com sucesso"
},
"error": {
"message": ""
"message": "Não foi possível transferir a propriedade"
}
}
},
"addMember": {
"label": ""
"label": "Adicionar membro"
},
"removeMember": {
"label": "",
"confirm": ""
"label": "Remover membro",
"confirm": "Você tem certeza que deseja remover {user} desse grupo?"
},
"delete": {
"label": "",
"description": "",
"confirm": "",
"label": "Deletar grupo",
"description": "Uma vez deletado o grupo não há como reverter. Tenha cuidado.",
"confirm": "Você tem certeza que deseja deletar o grupo {name}?",
"notification": {
"success": {
"message": ""
"message": "Grupo {name} deletado com sucesso"
},
"error": {
"message": ""
"message": "Não foi possível deletar o grupo {name}"
}
}
},
"changePermissions": {
"notification": {
"success": {
"title": "",
"message": ""
"title": "Permissões salvas",
"message": "Permissões foram salvas com sucesso"
},
"error": {
"title": "",
"message": ""
"title": "Permissões não salvas",
"message": "As permissões não foram salvas"
}
}
},
"update": {
"notification": {
"success": {
"message": ""
"message": "O grupo {name} foi salvo com sucesso"
},
"error": {
"message": ""
"message": "Não é possível salvar o grupo {name}"
}
}
},
"select": {
"label": "",
"notFound": ""
"label": "Selecionar grupo",
"notFound": "Nenhum grupo encontrado"
},
"settings": {
"board": {
"notification": {
"success": {
"title": "",
"title": "Configurações salvas",
"message": ""
},
"error": {
@@ -536,7 +536,7 @@
}
},
"defaultGroup": {
"name": "",
"name": "Grupo padrão",
"description": ""
}
},
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Поиск",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Hladať",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Sökning",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Ara",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Пошук",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "Tìm",
"defaultSearchEngine": {
+7
View File
@@ -3294,6 +3294,13 @@
}
}
},
"user": {
"title": "",
"enableGravatar": {
"label": "",
"description": ""
}
},
"search": {
"title": "搜尋",
"defaultSearchEngine": {
+7 -4
View File
@@ -27,12 +27,14 @@
"dependencies": {
"@homarr/common": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/settings": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/core": "^8.3.10",
"@mantine/dates": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@tabler/icons-react": "^3.36.0",
"@mantine/core": "^8.3.12",
"@mantine/dates": "^8.3.12",
"@mantine/hooks": "^8.3.12",
"@tabler/icons-react": "^3.36.1",
"crypto-js": "^4.2.0",
"mantine-react-table": "2.0.0-beta.9",
"next": "16.1.1",
"react": "19.2.3",
@@ -43,6 +45,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/crypto-js": "^4.2.2",
"@types/css-modules": "^1.0.5",
"eslint": "^9.39.2",
"typescript": "^5.9.3"
@@ -1,9 +1,15 @@
"use client";
import type { AvatarProps } from "@mantine/core";
import { Avatar } from "@mantine/core";
import { enc, MD5 } from "crypto-js";
import { useSettings } from "@homarr/settings";
export interface UserProps {
name: string | null;
image: string | null;
email: string | null;
}
interface UserAvatarProps {
@@ -12,10 +18,18 @@ interface UserAvatarProps {
}
export const UserAvatar = ({ user, size }: UserAvatarProps) => {
const { enableGravatar } = useSettings();
if (!user?.name) return <Avatar size={size} />;
if (user.image) {
return <Avatar src={user.image} alt={user.name} size={size} />;
}
if (user.email && enableGravatar) {
const emailHash = MD5(user.email.trim().toLowerCase()).toString(enc.Hex);
return <Avatar src={`https://seccdn.libravatar.org/avatar/${emailHash}?d=blank`} alt={user.name} size={size} />;
}
return <Avatar name={user.name} color="initials" size={size}></Avatar>;
};
+20 -20
View File
@@ -48,26 +48,26 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@mantine/charts": "^8.3.10",
"@mantine/core": "^8.3.10",
"@mantine/hooks": "^8.3.10",
"@tabler/icons-react": "^3.36.0",
"@tiptap/extension-color": "3.14.0",
"@tiptap/extension-highlight": "3.14.0",
"@tiptap/extension-image": "3.14.0",
"@tiptap/extension-link": "^3.14.0",
"@tiptap/extension-placeholder": "^3.14.0",
"@tiptap/extension-table": "3.14.0",
"@tiptap/extension-table-cell": "3.14.0",
"@tiptap/extension-table-header": "3.14.0",
"@tiptap/extension-table-row": "3.14.0",
"@tiptap/extension-task-item": "3.14.0",
"@tiptap/extension-task-list": "3.14.0",
"@tiptap/extension-text-align": "3.14.0",
"@tiptap/extension-text-style": "3.14.0",
"@tiptap/extension-underline": "3.14.0",
"@tiptap/react": "^3.14.0",
"@tiptap/starter-kit": "^3.14.0",
"@mantine/charts": "^8.3.12",
"@mantine/core": "^8.3.12",
"@mantine/hooks": "^8.3.12",
"@tabler/icons-react": "^3.36.1",
"@tiptap/extension-color": "3.15.3",
"@tiptap/extension-highlight": "3.15.3",
"@tiptap/extension-image": "3.15.3",
"@tiptap/extension-link": "^3.15.3",
"@tiptap/extension-placeholder": "^3.15.3",
"@tiptap/extension-table": "3.15.3",
"@tiptap/extension-table-cell": "3.15.3",
"@tiptap/extension-table-header": "3.15.3",
"@tiptap/extension-table-row": "3.15.3",
"@tiptap/extension-task-item": "3.15.3",
"@tiptap/extension-task-list": "3.15.3",
"@tiptap/extension-text-align": "3.15.3",
"@tiptap/extension-text-style": "3.15.3",
"@tiptap/extension-underline": "3.15.3",
"@tiptap/react": "^3.15.3",
"@tiptap/starter-kit": "^3.15.3",
"clsx": "^2.1.1",
"dayjs": "^1.11.19",
"mantine-form-zod-resolver": "^1.3.0",
+3 -1
View File
@@ -42,7 +42,9 @@ export interface WidgetDefinition {
icon: TablerIcon;
supportedIntegrations?: IntegrationKind[];
integrationsRequired?: boolean;
createOptions: (settings: SettingsContextProps) => WidgetOptionsRecord;
createOptions: (
settings: Pick<SettingsContextProps, "enableStatusByDefault" | "forceDisableStatus">,
) => WidgetOptionsRecord;
errors?: Partial<
Record<
DefaultErrorData["code"],
+1 -1
View File
@@ -115,7 +115,7 @@ export type inferSupportedIntegrationsStrict<TKind extends WidgetKind> = (Widget
export const reduceWidgetOptionsWithDefaultValues = (
kind: WidgetKind,
settings: SettingsContextProps,
settings: Pick<SettingsContextProps, "enableStatusByDefault" | "forceDisableStatus">,
currentValue: Record<string, unknown> = {},
) => {
const definition = widgetImports[kind].definition;