Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
088b4b58b8 | ||
|
|
3ff0d72ba1 | ||
|
|
a63a191203 | ||
|
|
1be906a0cd | ||
|
|
65faf99d9c | ||
|
|
4b35c31ecc | ||
|
|
63d8fa8c26 | ||
|
|
b5dd6ae8ef | ||
|
|
d540ad374a | ||
|
|
6a971c1986 | ||
|
|
d86fe3ddf0 | ||
|
|
06db8960a7 | ||
|
|
a5719acf34 | ||
|
|
d1d597c8e1 | ||
|
|
657e42e4bb | ||
|
|
256ebcb864 | ||
|
|
72e86dee01 | ||
|
|
b7910b16e0 | ||
|
|
5a83bb6e39 | ||
|
|
433b66582d | ||
|
|
acbcb6818b | ||
|
|
07d991a0a1 | ||
|
|
92364e9574 | ||
|
|
2991777b0a | ||
|
|
6cc2ac56a9 | ||
|
|
92086e060f | ||
|
|
2aa5ab95e6 | ||
|
|
650f761325 | ||
|
|
be61602576 | ||
|
|
d01834d975 | ||
|
|
4a67bf0ef3 | ||
|
|
4454e3fb7a |
No files matched your search
@@ -7,7 +7,7 @@
|
||||
"build": "pnpm with-env next build",
|
||||
"clean": "git clean -xdf .next .turbo node_modules",
|
||||
"dev": "pnpm with-env next dev",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"start": "pnpm with-env next start",
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
@@ -18,15 +18,7 @@ export const CustomMantineProvider = ({
|
||||
const manager = useColorSchemeManager();
|
||||
return (
|
||||
<DirectionProvider>
|
||||
<MantineProvider
|
||||
defaultColorScheme={defaultColorScheme}
|
||||
colorSchemeManager={manager}
|
||||
theme={createTheme({
|
||||
primaryColor: "red",
|
||||
autoContrast: true,
|
||||
defaultRadius: "sm",
|
||||
})}
|
||||
>
|
||||
<MantineProvider defaultColorScheme={defaultColorScheme} colorSchemeManager={manager} theme={theme}>
|
||||
{children}
|
||||
</MantineProvider>
|
||||
</DirectionProvider>
|
||||
|
||||
@@ -185,7 +185,7 @@ export default async function AboutPage() {
|
||||
<SimpleGrid cols={{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }} spacing="md">
|
||||
{Object.entries(dependencies)
|
||||
.filter(([, value]) => !value.includes("workspace:"))
|
||||
.toSorted(([key1], [key2]) => key1.localeCompare(key2))
|
||||
.sort(([key1], [key2]) => key1.localeCompare(key2))
|
||||
.map(([name, version]) => (
|
||||
<UnstyledButton
|
||||
key={name}
|
||||
|
||||
@@ -48,7 +48,7 @@ export default async function CertificatesPage({ params }: CertificatesPageProps
|
||||
} as const;
|
||||
}
|
||||
})
|
||||
.toSorted((certA, certB) => {
|
||||
.sort((certA, certB) => {
|
||||
if (certA.isError) return -1;
|
||||
if (certB.isError) return 1;
|
||||
return certA.x509.validToDate.getTime() - certB.x509.validToDate.getTime();
|
||||
|
||||
@@ -107,6 +107,23 @@ export function DockerTable(initialData: RouterOutputs["docker"]["getContainers"
|
||||
refetchOnReconnect: false,
|
||||
});
|
||||
const relativeTime = useTimeAgo(data.timestamp);
|
||||
const utils = clientApi.useUtils();
|
||||
const { mutate, isPending } = clientApi.docker.invalidate.useMutation({
|
||||
async onSuccess() {
|
||||
await utils.docker.getContainers.invalidate();
|
||||
showSuccessNotification({
|
||||
title: tDocker("action.refresh.notification.success.title"),
|
||||
message: tDocker("action.refresh.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
title: tDocker("action.refresh.notification.error.title"),
|
||||
message: tDocker("action.refresh.notification.error.message"),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const table = useTranslatedMantineReactTable({
|
||||
data: data.containers,
|
||||
enableDensityToggle: false,
|
||||
@@ -125,35 +142,11 @@ export function DockerTable(initialData: RouterOutputs["docker"]["getContainers"
|
||||
},
|
||||
|
||||
initialState: { density: "xs", showGlobalFilter: true },
|
||||
renderTopToolbarCustomActions: () => {
|
||||
const utils = clientApi.useUtils();
|
||||
const { mutate, isPending } = clientApi.docker.invalidate.useMutation({
|
||||
async onSuccess() {
|
||||
await utils.docker.getContainers.invalidate();
|
||||
showSuccessNotification({
|
||||
title: tDocker("action.refresh.notification.success.title"),
|
||||
message: tDocker("action.refresh.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
title: tDocker("action.refresh.notification.error.title"),
|
||||
message: tDocker("action.refresh.notification.error.message"),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="default"
|
||||
rightSection={<IconRefresh size="1rem" />}
|
||||
onClick={() => mutate()}
|
||||
loading={isPending}
|
||||
>
|
||||
{tDocker("action.refresh.label")}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
renderTopToolbarCustomActions: () => (
|
||||
<Button variant="default" rightSection={<IconRefresh size="1rem" />} onClick={() => mutate()} loading={isPending}>
|
||||
{tDocker("action.refresh.label")}
|
||||
</Button>
|
||||
),
|
||||
renderToolbarAlertBannerContent: ({ groupedAlert, table }) => {
|
||||
const dockerContainers = table.getSelectedRowModel().rows.map((row) => row.original);
|
||||
return (
|
||||
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Group, Stack } from "@mantine/core";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { userChangeHomeBoardsSchema } from "@homarr/validation/user";
|
||||
|
||||
import type { Board } from "~/app/[locale]/boards/_types";
|
||||
import { BoardSelect } from "~/components/board/board-select";
|
||||
|
||||
interface ChangeHomeBoardFormProps {
|
||||
user: RouterOutputs["user"]["getById"];
|
||||
boardsData: Pick<Board, "id" | "name" | "logoImageUrl">[];
|
||||
}
|
||||
|
||||
export const ChangeHomeBoardForm = ({ user, boardsData }: ChangeHomeBoardFormProps) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.user.changeHomeBoards.useMutation({
|
||||
async onSettled() {
|
||||
await revalidatePathActionAsync(`/manage/users/${user.id}`);
|
||||
},
|
||||
onSuccess(_, variables) {
|
||||
form.setInitialValues({
|
||||
homeBoardId: variables.homeBoardId,
|
||||
mobileHomeBoardId: variables.mobileHomeBoardId,
|
||||
});
|
||||
showSuccessNotification({
|
||||
message: t("user.action.changeHomeBoard.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
message: t("user.action.changeHomeBoard.notification.error.message"),
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userChangeHomeBoardsSchema, {
|
||||
initialValues: {
|
||||
homeBoardId: user.homeBoardId,
|
||||
mobileHomeBoardId: user.mobileHomeBoardId,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
mutate({
|
||||
userId: user.id,
|
||||
...values,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
<BoardSelect
|
||||
label={t("management.page.user.setting.general.item.board.type.general")}
|
||||
clearable
|
||||
boards={boardsData}
|
||||
w="100%"
|
||||
{...form.getInputProps("homeBoardId")}
|
||||
/>
|
||||
<BoardSelect
|
||||
label={t("management.page.user.setting.general.item.board.type.mobile")}
|
||||
clearable
|
||||
boards={boardsData}
|
||||
w="100%"
|
||||
{...form.getInputProps("mobileHomeBoardId")}
|
||||
/>
|
||||
|
||||
<Group justify="end">
|
||||
<Button type="submit" loading={isPending}>
|
||||
{t("common.action.save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = z.infer<typeof userChangeHomeBoardsSchema>;
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Group, Select, Stack, Switch } from "@mantine/core";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { userChangeSearchPreferencesSchema } from "@homarr/validation/user";
|
||||
|
||||
interface ChangeSearchPreferencesFormProps {
|
||||
user: RouterOutputs["user"]["getById"];
|
||||
searchEnginesData: { value: string; label: string }[];
|
||||
}
|
||||
|
||||
export const ChangeSearchPreferencesForm = ({ user, searchEnginesData }: ChangeSearchPreferencesFormProps) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.user.changeSearchPreferences.useMutation({
|
||||
async onSettled() {
|
||||
await revalidatePathActionAsync(`/manage/users/${user.id}`);
|
||||
},
|
||||
onSuccess(_, variables) {
|
||||
form.setInitialValues({
|
||||
defaultSearchEngineId: variables.defaultSearchEngineId,
|
||||
openInNewTab: variables.openInNewTab,
|
||||
ddgBangsEnabled: variables.ddgBangsEnabled,
|
||||
});
|
||||
showSuccessNotification({
|
||||
message: t("user.action.changeSearchPreferences.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
message: t("user.action.changeSearchPreferences.notification.error.message"),
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userChangeSearchPreferencesSchema, {
|
||||
initialValues: {
|
||||
defaultSearchEngineId: user.defaultSearchEngineId,
|
||||
openInNewTab: user.openSearchInNewTab,
|
||||
ddgBangsEnabled: user.ddgBangs,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
mutate({
|
||||
userId: user.id,
|
||||
...values,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
<Select
|
||||
label={t("user.field.defaultSearchEngine.label")}
|
||||
w="100%"
|
||||
data={searchEnginesData}
|
||||
{...form.getInputProps("defaultSearchEngineId")}
|
||||
/>
|
||||
<Switch
|
||||
label={t("user.field.openSearchInNewTab.label")}
|
||||
{...form.getInputProps("openInNewTab", { type: "checkbox" })}
|
||||
/>
|
||||
<Switch
|
||||
label={t("user.field.ddgBangs.label")}
|
||||
{...form.getInputProps("ddgBangsEnabled", { type: "checkbox" })}
|
||||
/>
|
||||
|
||||
<Group justify="end">
|
||||
<Button type="submit" loading={isPending}>
|
||||
{t("common.action.save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = z.infer<typeof userChangeSearchPreferencesSchema>;
|
||||
-83
@@ -1,83 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Group, Radio, Stack } from "@mantine/core";
|
||||
import type { DayOfWeek } from "@mantine/dates";
|
||||
import dayjs from "dayjs";
|
||||
import localeData from "dayjs/plugin/localeData";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { userFirstDayOfWeekSchema } from "@homarr/validation/user";
|
||||
|
||||
dayjs.extend(localeData);
|
||||
|
||||
interface FirstDayOfWeekProps {
|
||||
user: RouterOutputs["user"]["getById"];
|
||||
}
|
||||
|
||||
const weekDays = dayjs.weekdays(false);
|
||||
|
||||
export const FirstDayOfWeek = ({ user }: FirstDayOfWeekProps) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.user.changeFirstDayOfWeek.useMutation({
|
||||
async onSettled() {
|
||||
await revalidatePathActionAsync(`/manage/users/${user.id}`);
|
||||
},
|
||||
onSuccess(_, variables) {
|
||||
form.setInitialValues({
|
||||
firstDayOfWeek: variables.firstDayOfWeek as DayOfWeek,
|
||||
});
|
||||
showSuccessNotification({
|
||||
message: t("user.action.changeFirstDayOfWeek.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
message: t("user.action.changeFirstDayOfWeek.notification.error.message"),
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userFirstDayOfWeekSchema, {
|
||||
initialValues: {
|
||||
firstDayOfWeek: user.firstDayOfWeek as DayOfWeek,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
mutate({
|
||||
id: user.id,
|
||||
...values,
|
||||
});
|
||||
};
|
||||
|
||||
const inputProps = form.getInputProps("firstDayOfWeek");
|
||||
const onChange = inputProps.onChange as (value: number) => void;
|
||||
const value = (inputProps.value as number).toString();
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
<Radio.Group {...inputProps} value={value} onChange={(nextValue: string) => onChange(parseInt(nextValue, 10))}>
|
||||
<Group mt="xs">
|
||||
<Radio value="1" label={weekDays[1]} />
|
||||
<Radio value="6" label={weekDays[6]} />
|
||||
<Radio value="0" label={weekDays[0]} />
|
||||
</Group>
|
||||
</Radio.Group>
|
||||
|
||||
<Group justify="end">
|
||||
<Button type="submit" loading={isPending}>
|
||||
{t("common.action.save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = z.infer<typeof userFirstDayOfWeekSchema>;
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Group,
|
||||
Radio,
|
||||
Select,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Switch,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import type { DayOfWeek } from "@mantine/dates";
|
||||
import dayjs from "dayjs";
|
||||
import localeData from "dayjs/plugin/localeData";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { env } from "@homarr/common/env";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import {
|
||||
userChangeHomeBoardsSchema,
|
||||
userChangeSearchPreferencesSchema,
|
||||
userEditProfileSchema,
|
||||
userFirstDayOfWeekSchema,
|
||||
userPingIconsEnabledSchema,
|
||||
} from "@homarr/validation/user";
|
||||
|
||||
import type { Board } from "~/app/[locale]/boards/_types";
|
||||
import { BoardSelect } from "~/components/board/board-select";
|
||||
import { CurrentLanguageCombobox } from "~/components/language/current-language-combobox";
|
||||
|
||||
dayjs.extend(localeData);
|
||||
|
||||
const userGeneralSettingsSchema = z.object({
|
||||
name: userEditProfileSchema.shape.name,
|
||||
email: userEditProfileSchema.shape.email,
|
||||
homeBoardId: userChangeHomeBoardsSchema.shape.homeBoardId,
|
||||
mobileHomeBoardId: userChangeHomeBoardsSchema.shape.mobileHomeBoardId,
|
||||
defaultSearchEngineId: userChangeSearchPreferencesSchema.shape.defaultSearchEngineId,
|
||||
openInNewTab: userChangeSearchPreferencesSchema.shape.openInNewTab,
|
||||
ddgBangsEnabled: userChangeSearchPreferencesSchema.shape.ddgBangsEnabled,
|
||||
firstDayOfWeek: userFirstDayOfWeekSchema.shape.firstDayOfWeek,
|
||||
pingIconsEnabled: userPingIconsEnabledSchema.shape.pingIconsEnabled,
|
||||
});
|
||||
|
||||
type FormValues = z.infer<typeof userGeneralSettingsSchema>;
|
||||
|
||||
const FIRST_DAY_OPTIONS: { value: DayOfWeek; labelKey: number }[] = [
|
||||
{ value: 1, labelKey: 1 },
|
||||
{ value: 6, labelKey: 6 },
|
||||
{ value: 0, labelKey: 0 },
|
||||
];
|
||||
|
||||
interface UserGeneralSettingsFormProps {
|
||||
user: RouterOutputs["user"]["getById"];
|
||||
boardsData: Pick<Board, "id" | "name" | "logoImageUrl">[];
|
||||
searchEnginesData: { value: string; label: string }[];
|
||||
showLanguageSelector?: boolean;
|
||||
}
|
||||
|
||||
const buildInitialValues = (user: RouterOutputs["user"]["getById"]): FormValues => ({
|
||||
name: user.name ?? "",
|
||||
email: user.email ?? "",
|
||||
homeBoardId: user.homeBoardId,
|
||||
mobileHomeBoardId: user.mobileHomeBoardId,
|
||||
defaultSearchEngineId: user.defaultSearchEngineId,
|
||||
openInNewTab: user.openSearchInNewTab,
|
||||
ddgBangsEnabled: user.ddgBangs,
|
||||
firstDayOfWeek: user.firstDayOfWeek as DayOfWeek,
|
||||
pingIconsEnabled: user.pingIconsEnabled,
|
||||
});
|
||||
|
||||
export const UserGeneralSettingsForm = ({
|
||||
user,
|
||||
boardsData,
|
||||
searchEnginesData,
|
||||
showLanguageSelector = false,
|
||||
}: UserGeneralSettingsFormProps) => {
|
||||
const t = useI18n();
|
||||
const tGeneral = useScopedI18n("management.page.user.setting.general");
|
||||
const isCredentialsUser = user.provider === "credentials";
|
||||
|
||||
const editProfileMutation = clientApi.user.editProfile.useMutation();
|
||||
const changeHomeBoardsMutation = clientApi.user.changeHomeBoards.useMutation();
|
||||
const changeSearchPreferencesMutation = clientApi.user.changeSearchPreferences.useMutation();
|
||||
const changeFirstDayOfWeekMutation = clientApi.user.changeFirstDayOfWeek.useMutation();
|
||||
const changePingIconsEnabledMutation = clientApi.user.changePingIconsEnabled.useMutation();
|
||||
|
||||
const initialValues = buildInitialValues(user);
|
||||
const initialValuesRef = useRef(initialValues);
|
||||
|
||||
const form = useZodForm(userGeneralSettingsSchema, {
|
||||
initialValues,
|
||||
});
|
||||
|
||||
const isDirtyRef = useRef(false);
|
||||
isDirtyRef.current = form.isDirty();
|
||||
|
||||
const weekDays = useMemo(() => dayjs.weekdays(false), []);
|
||||
|
||||
const mutations = [
|
||||
editProfileMutation,
|
||||
changeHomeBoardsMutation,
|
||||
changeSearchPreferencesMutation,
|
||||
changeFirstDayOfWeekMutation,
|
||||
changePingIconsEnabledMutation,
|
||||
];
|
||||
const isPending = mutations.some((m) => m.isPending);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: BeforeUnloadEvent) => {
|
||||
if (env.NODE_ENV === "development") return;
|
||||
if (isDirtyRef.current) e.preventDefault();
|
||||
};
|
||||
window.addEventListener("beforeunload", handler);
|
||||
return () => window.removeEventListener("beforeunload", handler);
|
||||
}, []);
|
||||
|
||||
const handleSubmitAsync = async (values: FormValues) => {
|
||||
const parsed = userGeneralSettingsSchema.safeParse(values);
|
||||
if (!parsed.success) return;
|
||||
|
||||
const defaults = initialValuesRef.current;
|
||||
const changed = <K extends keyof FormValues>(...fields: K[]) =>
|
||||
fields.some((f) => (f === "email" ? (values[f] || null) !== (defaults[f] || null) : values[f] !== defaults[f]));
|
||||
|
||||
const saveActions: { when: boolean; action: () => Promise<unknown> }[] = [
|
||||
{
|
||||
when: isCredentialsUser && changed("name", "email"),
|
||||
action: () =>
|
||||
editProfileMutation.mutateAsync({ id: user.id, name: parsed.data.name, email: parsed.data.email ?? "" }),
|
||||
},
|
||||
{
|
||||
when: changed("homeBoardId", "mobileHomeBoardId"),
|
||||
action: () =>
|
||||
changeHomeBoardsMutation.mutateAsync({
|
||||
userId: user.id,
|
||||
homeBoardId: parsed.data.homeBoardId,
|
||||
mobileHomeBoardId: parsed.data.mobileHomeBoardId,
|
||||
}),
|
||||
},
|
||||
{
|
||||
when: changed("defaultSearchEngineId", "openInNewTab", "ddgBangsEnabled"),
|
||||
action: () =>
|
||||
changeSearchPreferencesMutation.mutateAsync({
|
||||
userId: user.id,
|
||||
defaultSearchEngineId: parsed.data.defaultSearchEngineId,
|
||||
openInNewTab: parsed.data.openInNewTab,
|
||||
ddgBangsEnabled: parsed.data.ddgBangsEnabled,
|
||||
}),
|
||||
},
|
||||
{
|
||||
when: changed("firstDayOfWeek"),
|
||||
action: () =>
|
||||
changeFirstDayOfWeekMutation.mutateAsync({ id: user.id, firstDayOfWeek: parsed.data.firstDayOfWeek }),
|
||||
},
|
||||
{
|
||||
when: changed("pingIconsEnabled"),
|
||||
action: () =>
|
||||
changePingIconsEnabledMutation.mutateAsync({ id: user.id, pingIconsEnabled: parsed.data.pingIconsEnabled }),
|
||||
},
|
||||
];
|
||||
|
||||
const promises = saveActions.filter((s) => s.when).map((s) => s.action());
|
||||
if (promises.length === 0) return;
|
||||
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
const newValues = { ...parsed.data, email: parsed.data.email ?? "" };
|
||||
initialValuesRef.current = newValues;
|
||||
form.setInitialValues(newValues);
|
||||
form.resetDirty();
|
||||
await revalidatePathActionAsync(`/manage/users/${user.id}`);
|
||||
showSuccessNotification({
|
||||
title: t("common.notification.update.success"),
|
||||
message: t("common.notification.update.success"),
|
||||
});
|
||||
} catch {
|
||||
showErrorNotification({
|
||||
title: t("common.notification.update.error"),
|
||||
message: t("common.notification.update.error"),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleDiscard = () => {
|
||||
form.setValues(initialValuesRef.current);
|
||||
form.resetDirty();
|
||||
};
|
||||
|
||||
const firstDayOfWeekInputProps = form.getInputProps("firstDayOfWeek");
|
||||
const firstDayOfWeekOnChange = firstDayOfWeekInputProps.onChange as (value: number) => void;
|
||||
const firstDayOfWeekValue = (firstDayOfWeekInputProps.value as number).toString();
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmitAsync)}>
|
||||
<Stack gap="lg">
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }} spacing="lg" verticalSpacing="lg">
|
||||
<Card withBorder bg="transparent">
|
||||
<Stack gap="md">
|
||||
<Stack gap={2}>
|
||||
<Title order={3}>{t("user.name")}</Title>
|
||||
{!isCredentialsUser && (
|
||||
<Text c="dimmed" size="sm">
|
||||
{t("management.page.user.fieldsDisabledExternalProvider")}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
<Divider />
|
||||
<TextInput
|
||||
disabled={!isCredentialsUser}
|
||||
label={t("user.field.username.label")}
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
disabled={!isCredentialsUser}
|
||||
label={t("user.field.email.label")}
|
||||
{...form.getInputProps("email")}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
<Card withBorder bg="transparent">
|
||||
<Stack gap="md">
|
||||
<Stack gap={2}>
|
||||
<Title order={3}>{tGeneral("item.board.title")}</Title>
|
||||
</Stack>
|
||||
<Divider />
|
||||
<BoardSelect
|
||||
label={tGeneral("item.board.type.general")}
|
||||
clearable
|
||||
boards={boardsData}
|
||||
w="100%"
|
||||
withinPortal
|
||||
{...form.getInputProps("homeBoardId")}
|
||||
/>
|
||||
<BoardSelect
|
||||
label={tGeneral("item.board.type.mobile")}
|
||||
clearable
|
||||
boards={boardsData}
|
||||
w="100%"
|
||||
withinPortal
|
||||
{...form.getInputProps("mobileHomeBoardId")}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
<Card withBorder bg="transparent">
|
||||
<Stack gap="md">
|
||||
<Stack gap={2}>
|
||||
<Title order={3}>{tGeneral("item.search")}</Title>
|
||||
</Stack>
|
||||
<Divider />
|
||||
<Select
|
||||
label={t("user.field.defaultSearchEngine.label")}
|
||||
w="100%"
|
||||
data={searchEnginesData}
|
||||
comboboxProps={{ withinPortal: true }}
|
||||
{...form.getInputProps("defaultSearchEngineId")}
|
||||
/>
|
||||
<Switch
|
||||
label={t("user.field.openSearchInNewTab.label")}
|
||||
{...form.getInputProps("openInNewTab", { type: "checkbox" })}
|
||||
/>
|
||||
<Switch
|
||||
label={t("user.field.ddgBangs.label")}
|
||||
{...form.getInputProps("ddgBangsEnabled", { type: "checkbox" })}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
|
||||
<Card withBorder bg="transparent">
|
||||
<Stack gap="md">
|
||||
<Stack gap={2}>
|
||||
<Title order={3}>{tGeneral("item.language")}</Title>
|
||||
</Stack>
|
||||
<Divider />
|
||||
{showLanguageSelector && <CurrentLanguageCombobox withinPortal />}
|
||||
<Title order={4}>{tGeneral("item.firstDayOfWeek")}</Title>
|
||||
<Radio.Group
|
||||
{...firstDayOfWeekInputProps}
|
||||
value={firstDayOfWeekValue}
|
||||
onChange={(nextValue: string) => firstDayOfWeekOnChange(parseInt(nextValue, 10))}
|
||||
>
|
||||
<Group mt="xs" wrap="wrap">
|
||||
{FIRST_DAY_OPTIONS.map(({ value: dayValue, labelKey }) => (
|
||||
<Radio key={dayValue} value={dayValue.toString()} label={weekDays[labelKey]} />
|
||||
))}
|
||||
</Group>
|
||||
</Radio.Group>
|
||||
<Divider my="xs" />
|
||||
<Title order={4}>{tGeneral("item.accessibility")}</Title>
|
||||
<Switch
|
||||
label={t("user.field.pingIconsEnabled.label")}
|
||||
{...form.getInputProps("pingIconsEnabled", { type: "checkbox" })}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
</SimpleGrid>
|
||||
|
||||
<div style={{ position: "sticky", bottom: 20 }}>
|
||||
{form.isDirty() && (
|
||||
<Card withBorder>
|
||||
<Group justify="space-between" wrap="wrap">
|
||||
<Text fw={500}>{t("common.unsavedChanges")}</Text>
|
||||
<Group>
|
||||
<Button disabled={isPending} variant="default" onClick={handleDiscard}>
|
||||
{t("common.action.discard")}
|
||||
</Button>
|
||||
<Button loading={isPending} type="submit" disabled={!form.isValid()}>
|
||||
{t("common.action.saveChanges")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Group>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { Button, Group, Stack, Switch } from "@mantine/core";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { userPingIconsEnabledSchema } from "@homarr/validation/user";
|
||||
|
||||
interface PingIconsEnabledProps {
|
||||
user: RouterOutputs["user"]["getById"];
|
||||
}
|
||||
|
||||
export const PingIconsEnabled = ({ user }: PingIconsEnabledProps) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.user.changePingIconsEnabled.useMutation({
|
||||
async onSettled() {
|
||||
await revalidatePathActionAsync(`/manage/users/${user.id}`);
|
||||
},
|
||||
onSuccess(_, variables) {
|
||||
form.setInitialValues({
|
||||
pingIconsEnabled: variables.pingIconsEnabled,
|
||||
});
|
||||
showSuccessNotification({
|
||||
message: t("user.action.changePingIconsEnabled.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError() {
|
||||
showErrorNotification({
|
||||
message: t("user.action.changePingIconsEnabled.notification.error.message"),
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userPingIconsEnabledSchema, {
|
||||
initialValues: {
|
||||
pingIconsEnabled: user.pingIconsEnabled,
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormType) => {
|
||||
mutate({
|
||||
id: user.id,
|
||||
...values,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack gap="md">
|
||||
<Switch
|
||||
{...form.getInputProps("pingIconsEnabled", { type: "checkbox" })}
|
||||
label={t("user.field.pingIconsEnabled.label")}
|
||||
/>
|
||||
|
||||
<Group justify="end">
|
||||
<Button type="submit" loading={isPending}>
|
||||
{t("common.action.save")}
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = z.infer<typeof userPingIconsEnabledSchema>;
|
||||
+5
-4
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { Box, Button, FileButton, Menu, UnstyledButton } from "@mantine/core";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { useDisclosure, useMediaQuery } from "@mantine/hooks";
|
||||
import { IconPencil, IconPhotoEdit, IconPhotoX } from "@tabler/icons-react";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
@@ -25,6 +25,7 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
|
||||
},
|
||||
});
|
||||
const [opened, { toggle }] = useDisclosure(false);
|
||||
const isMobile = useMediaQuery("(max-width: 48em)");
|
||||
const { openConfirmModal } = useConfirmModal();
|
||||
const t = useI18n();
|
||||
const tManageAvatar = useScopedI18n("user.action.manageAvatar");
|
||||
@@ -94,11 +95,11 @@ export const UserProfileAvatarForm = ({ user }: UserProfileAvatarForm) => {
|
||||
}, [mutate, user.id, openConfirmModal, tManageAvatar]);
|
||||
|
||||
return (
|
||||
<Box pos="relative">
|
||||
<Menu opened={opened} keepMounted onChange={toggle} position="bottom-start">
|
||||
<Box pos="relative" display="flex" style={{ justifyContent: isMobile ? "center" : undefined }}>
|
||||
<Menu opened={opened} keepMounted onChange={toggle} position="bottom-start" withArrow withinPortal>
|
||||
<Menu.Target>
|
||||
<UnstyledButton onClick={toggle}>
|
||||
<UserAvatar user={user} size={200} />
|
||||
<UserAvatar user={user} size={isMobile ? 120 : 200} />
|
||||
<Button
|
||||
component="div"
|
||||
pos="absolute"
|
||||
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { Button, Group, Stack, TextInput } from "@mantine/core";
|
||||
|
||||
import type { RouterInputs, RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { userEditProfileSchema } from "@homarr/validation/user";
|
||||
|
||||
interface UserProfileFormProps {
|
||||
user: RouterOutputs["user"]["getById"];
|
||||
}
|
||||
|
||||
export const UserProfileForm = ({ user }: UserProfileFormProps) => {
|
||||
const t = useI18n();
|
||||
const { mutate, isPending } = clientApi.user.editProfile.useMutation({
|
||||
async onSettled() {
|
||||
await revalidatePathActionAsync("/manage/users");
|
||||
},
|
||||
onSuccess(_, variables) {
|
||||
// Reset form initial values to reset dirty state
|
||||
form.setInitialValues({
|
||||
name: variables.name,
|
||||
email: variables.email ?? "",
|
||||
});
|
||||
showSuccessNotification({
|
||||
title: t("common.notification.update.success"),
|
||||
message: t("user.action.editProfile.notification.success.message"),
|
||||
});
|
||||
},
|
||||
onError(error) {
|
||||
const message =
|
||||
error.data?.code === "CONFLICT"
|
||||
? t("user.error.usernameTaken")
|
||||
: t("user.action.editProfile.notification.error.message");
|
||||
showErrorNotification({
|
||||
title: t("common.notification.update.error"),
|
||||
message,
|
||||
});
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userEditProfileSchema.omit({ id: true }), {
|
||||
initialValues: {
|
||||
name: user.name ?? "",
|
||||
email: user.email ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
// Only credentials users can edit their profile
|
||||
const isProviderCredentials = user.provider === "credentials";
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(values: FormType) => {
|
||||
if (!isProviderCredentials) return;
|
||||
mutate({
|
||||
...values,
|
||||
id: user.id,
|
||||
});
|
||||
},
|
||||
[isProviderCredentials, mutate, user.id],
|
||||
);
|
||||
|
||||
return (
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
disabled={!isProviderCredentials}
|
||||
label={t("user.field.username.label")}
|
||||
withAsterisk
|
||||
{...form.getInputProps("name")}
|
||||
/>
|
||||
<TextInput
|
||||
disabled={!isProviderCredentials}
|
||||
label={t("user.field.email.label")}
|
||||
{...form.getInputProps("email")}
|
||||
/>
|
||||
|
||||
{isProviderCredentials && (
|
||||
<Group justify="end">
|
||||
<Button type="submit" disabled={!form.isDirty()} loading={isPending}>
|
||||
{t("common.action.saveChanges")}
|
||||
</Button>
|
||||
</Group>
|
||||
)}
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
type FormType = Omit<RouterInputs["user"]["editProfile"], "id">;
|
||||
@@ -6,18 +6,13 @@ import { api } from "@homarr/api/server";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||
|
||||
import { CurrentLanguageCombobox } from "~/components/language/current-language-combobox";
|
||||
import { DangerZoneItem, DangerZoneRoot } from "~/components/manage/danger-zone";
|
||||
import { catchTrpcNotFound } from "~/errors/trpc-catch-error";
|
||||
import { createMetaTitle } from "~/metadata";
|
||||
import { canAccessUserEditPage } from "../access";
|
||||
import { ChangeHomeBoardForm } from "./_components/_change-home-board";
|
||||
import { ChangeSearchPreferencesForm } from "./_components/_change-search-preferences";
|
||||
import { DeleteUserButton } from "./_components/_delete-user-button";
|
||||
import { FirstDayOfWeek } from "./_components/_first-day-of-week";
|
||||
import { PingIconsEnabled } from "./_components/_ping-icons-enabled";
|
||||
import { UserGeneralSettingsForm } from "./_components/_general-settings-form";
|
||||
import { UserProfileAvatarForm } from "./_components/_profile-avatar-form";
|
||||
import { UserProfileForm } from "./_components/_profile-form";
|
||||
|
||||
interface Props {
|
||||
params: Promise<{
|
||||
@@ -63,7 +58,7 @@ export default async function EditUserPage(props: Props) {
|
||||
|
||||
const boards = await api.board.getAllBoards();
|
||||
const searchEngines = await api.searchEngine.getSelectable();
|
||||
|
||||
const isSelf = session?.user.id === user.id;
|
||||
const isCredentialsUser = user.provider === "credentials";
|
||||
|
||||
return (
|
||||
@@ -73,51 +68,25 @@ export default async function EditUserPage(props: Props) {
|
||||
{t("management.page.user.fieldsDisabledExternalProvider")}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Title>{tGeneral("title")}</Title>
|
||||
<Group gap="xl">
|
||||
<Box flex={1}>
|
||||
<UserProfileForm user={user} />
|
||||
<Group gap="xl" align="flex-start" wrap="wrap">
|
||||
<Box flex={1} miw={{ base: "100%", md: 540 }}>
|
||||
<UserGeneralSettingsForm
|
||||
user={user}
|
||||
boardsData={boards.map((board) => ({
|
||||
id: board.id,
|
||||
name: board.name,
|
||||
logoImageUrl: board.logoImageUrl,
|
||||
}))}
|
||||
searchEnginesData={searchEngines}
|
||||
showLanguageSelector={isSelf}
|
||||
/>
|
||||
</Box>
|
||||
<Box w={{ base: "100%", lg: 200 }}>
|
||||
<Box w={{ base: "100%", lg: 260 }}>
|
||||
<UserProfileAvatarForm user={user} />
|
||||
</Box>
|
||||
</Group>
|
||||
|
||||
{session?.user.id === user.id && (
|
||||
<Stack mb="lg">
|
||||
<Title order={2}>{tGeneral("item.language")}</Title>
|
||||
<CurrentLanguageCombobox />
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Stack mb="lg">
|
||||
<Title order={2}>{tGeneral("item.board.title")}</Title>
|
||||
<ChangeHomeBoardForm
|
||||
user={user}
|
||||
boardsData={boards.map((board) => ({
|
||||
id: board.id,
|
||||
name: board.name,
|
||||
logoImageUrl: board.logoImageUrl,
|
||||
}))}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
<Stack mb="lg">
|
||||
<Title order={2}>{tGeneral("item.search")}</Title>
|
||||
<ChangeSearchPreferencesForm user={user} searchEnginesData={searchEngines} />
|
||||
</Stack>
|
||||
|
||||
<Stack mb="lg">
|
||||
<Title order={2}>{tGeneral("item.firstDayOfWeek")}</Title>
|
||||
<FirstDayOfWeek user={user} />
|
||||
</Stack>
|
||||
|
||||
<Stack mb="lg">
|
||||
<Title order={2}>{tGeneral("item.accessibility")}</Title>
|
||||
<PingIconsEnabled user={user} />
|
||||
</Stack>
|
||||
|
||||
<DangerZoneRoot>
|
||||
<DangerZoneItem
|
||||
label={t("user.action.delete.label")}
|
||||
|
||||
@@ -6,11 +6,20 @@ import { LanguageCombobox } from "./language-combobox";
|
||||
|
||||
interface CurrentLanguageComboboxProps {
|
||||
width?: string;
|
||||
withinPortal?: boolean;
|
||||
}
|
||||
|
||||
export const CurrentLanguageCombobox = ({ width }: CurrentLanguageComboboxProps) => {
|
||||
export const CurrentLanguageCombobox = ({ width, withinPortal }: CurrentLanguageComboboxProps) => {
|
||||
const currentLocale = useCurrentLocale();
|
||||
const { changeLocale, isPending } = useChangeLocale();
|
||||
|
||||
return <LanguageCombobox value={currentLocale} onChange={changeLocale} isPending={isPending} width={width} />;
|
||||
return (
|
||||
<LanguageCombobox
|
||||
value={currentLocale}
|
||||
onChange={changeLocale}
|
||||
isPending={isPending}
|
||||
width={width}
|
||||
withinPortal={withinPortal}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -16,9 +16,17 @@ interface LanguageComboboxProps {
|
||||
onChange: (value: SupportedLanguage) => void;
|
||||
isPending?: boolean;
|
||||
width?: string;
|
||||
withinPortal?: boolean;
|
||||
}
|
||||
|
||||
export const LanguageCombobox = ({ label, value, onChange, isPending, width }: LanguageComboboxProps) => {
|
||||
export const LanguageCombobox = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
isPending,
|
||||
width,
|
||||
withinPortal = false,
|
||||
}: LanguageComboboxProps) => {
|
||||
const combobox = useCombobox({
|
||||
onDropdownClose: () => combobox.resetSelectedOption(),
|
||||
});
|
||||
@@ -39,7 +47,7 @@ export const LanguageCombobox = ({ label, value, onChange, isPending, width }: L
|
||||
}, [combobox]);
|
||||
|
||||
return (
|
||||
<Combobox store={combobox} onOptionSubmit={handleOnOptionSubmit}>
|
||||
<Combobox store={combobox} withinPortal={withinPortal} onOptionSubmit={handleOnOptionSubmit}>
|
||||
<Combobox.Target>
|
||||
<InputBase
|
||||
component="button"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"dev": "pnpm with-env tsx ./src/main.ts",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"dev": "pnpm with-env tsx ./src/main.ts",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"with-env": "dotenv -e ../../.env --"
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.16.0",
|
||||
"pnpm": ">=10.33.4"
|
||||
"pnpm": ">=10.34.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"packageManager": "pnpm@10.34.1",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"better-sqlite3"
|
||||
|
||||
+8
-8
@@ -17,8 +17,8 @@
|
||||
"db:studio": "pnpm -F db studio",
|
||||
"dev": "pnpm -F nextjs dev",
|
||||
"docker:dev": "docker compose -f ./development/development.docker-compose.yml up",
|
||||
"format": "turbo format --continue",
|
||||
"format:fix": "turbo format --continue -- --write",
|
||||
"format": "turbo format --continue -- --check",
|
||||
"format:fix": "turbo format --continue",
|
||||
"lint": "turbo lint --continue",
|
||||
"lint:fix": "turbo lint --continue -- --fix",
|
||||
"lint:ws": "pnpm dlx sherif@latest",
|
||||
@@ -60,9 +60,9 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.16.0",
|
||||
"pnpm": ">=10.33.4"
|
||||
"pnpm": ">=10.34.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.4",
|
||||
"packageManager": "pnpm@10.34.1",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@tree-sitter-grammars/tree-sitter-yaml",
|
||||
@@ -76,14 +76,14 @@
|
||||
"tree-sitter-json"
|
||||
],
|
||||
"overrides": {
|
||||
"@babel/helpers@<7.26.10": ">=7.29.2",
|
||||
"@babel/runtime@<7.26.10": ">=7.29.2",
|
||||
"@babel/helpers@<7.26.10": ">=7.29.7",
|
||||
"@babel/runtime@<7.26.10": ">=7.29.7",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.16.1",
|
||||
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.6",
|
||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.6",
|
||||
"esbuild@<=0.24.2": ">=0.28.0",
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
|
||||
"hono@<4.6.5": ">=4.12.22",
|
||||
"hono@<4.6.5": ">=4.12.23",
|
||||
"linkifyjs@<4.3.2": ">=4.3.3",
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.11",
|
||||
"prismjs@<1.30.0": ">=1.30.0",
|
||||
@@ -93,7 +93,7 @@
|
||||
"sha.js@<=2.4.11": ">=2.4.12",
|
||||
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
|
||||
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
|
||||
"tmp@<=0.2.3": ">=0.2.5",
|
||||
"tmp@<=0.2.3": ">=0.2.7",
|
||||
"vite@>=5.0.0 <=5.4.18": ">=8.0.14"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -40,6 +40,9 @@ export const env = createEnv({
|
||||
AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE: z.string().optional(),
|
||||
AUTH_OIDC_FORCE_USERINFO: createBooleanSchema(false),
|
||||
AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING: createBooleanSchema(false),
|
||||
AUTH_OIDC_TOKEN_ENDPOINT_AUTH_METHOD: z
|
||||
.enum(["client_secret_basic", "client_secret_post", "client_secret_jwt", "none"])
|
||||
.default("client_secret_basic"),
|
||||
}
|
||||
: {}),
|
||||
...(authProviders.includes("ldap")
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -14,6 +14,9 @@ export const OidcProvider = (headers: ReadonlyHeaders | null): OIDCConfig<Profil
|
||||
type: "oidc",
|
||||
clientId: env.AUTH_OIDC_CLIENT_ID,
|
||||
clientSecret: env.AUTH_OIDC_CLIENT_SECRET,
|
||||
client: {
|
||||
token_endpoint_auth_method: env.AUTH_OIDC_TOKEN_ENDPOINT_AUTH_METHOD,
|
||||
},
|
||||
issuer: env.AUTH_OIDC_ISSUER,
|
||||
allowDangerousEmailAccountLinking: env.AUTH_OIDC_ENABLE_DANGEROUS_ACCOUNT_LINKING,
|
||||
authorization: {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"scripts": {
|
||||
"build": "esbuild src/index.ts --bundle --platform=node --outfile=cli.cjs --external:bcrypt --external:cpu-features --loader:.html=text --loader:.node=text",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"run": "pnpm with-env node cli.cjs",
|
||||
"typecheck": "tsc --noEmit",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { EVERY_HOUR } from "@homarr/cron-jobs-core/expressions";
|
||||
import { EVERY_DAY } from "@homarr/cron-jobs-core/expressions";
|
||||
import { updateCheckerRequestHandler } from "@homarr/request-handler/update-checker";
|
||||
|
||||
import { createCronJob } from "../lib";
|
||||
|
||||
export const updateCheckerJob = createCronJob("updateChecker", EVERY_HOUR, {
|
||||
export const updateCheckerJob = createCronJob("updateChecker", EVERY_DAY, {
|
||||
runOnStart: true,
|
||||
}).withCallback(async () => {
|
||||
const handler = updateCheckerRequestHandler.handler({});
|
||||
await handler.getCachedOrUpdatedDataAsync({
|
||||
forceUpdate: true,
|
||||
});
|
||||
await handler.getCachedOrUpdatedDataAsync({});
|
||||
});
|
||||
@@ -20,7 +20,7 @@
|
||||
"build:postgresql": "esbuild migrations/postgresql/migrate.ts --bundle --platform=node --outfile=migrations/postgresql/migrate.cjs",
|
||||
"build:sqlite": "esbuild migrations/sqlite/migrate.ts --bundle --platform=node --outfile=migrations/sqlite/migrate.cjs",
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"migration:custom": "pnpm with-env tsx ./migrations/custom/run-custom.ts",
|
||||
"migration:mysql:drop": "pnpm with-env drizzle-kit drop --config ./configs/mysql.config.ts",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"postinstall": "tsx ./src/docs/codegen.ts",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import dayjs from "dayjs";
|
||||
import type { Duration } from "dayjs/plugin/duration";
|
||||
|
||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import type { createChannelWithLatestAndEvents } from "@homarr/redis";
|
||||
|
||||
@@ -15,6 +16,7 @@ interface Options<TData, TInput extends Record<string, unknown>> {
|
||||
options: Options<TData, TInput>,
|
||||
) => ReturnType<typeof createChannelWithLatestAndEvents<TData>>;
|
||||
cacheDuration: Duration;
|
||||
fallbackToStaleOnError?: boolean;
|
||||
}
|
||||
|
||||
export const createCachedRequestHandler = <TData, TInput extends Record<string, unknown>>(
|
||||
@@ -26,13 +28,33 @@ export const createCachedRequestHandler = <TData, TInput extends Record<string,
|
||||
|
||||
return {
|
||||
async getCachedOrUpdatedDataAsync({ forceUpdate = false }) {
|
||||
const channelData = await channel.getAsync();
|
||||
|
||||
const requestNewDataAsync = async () => {
|
||||
const data = await options.requestAsync(input);
|
||||
await channel.publishAndUpdateLastStateAsync(data);
|
||||
return {
|
||||
data,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
try {
|
||||
const data = await options.requestAsync(input);
|
||||
await channel.publishAndUpdateLastStateAsync(data);
|
||||
return {
|
||||
data,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
} catch (error) {
|
||||
if (options.fallbackToStaleOnError && channelData) {
|
||||
logger.warn(
|
||||
new ErrorWithMetadata(
|
||||
"Cached request handler using stale cache after fetch failure",
|
||||
{
|
||||
channel: channel.name,
|
||||
queryKey: options.queryKey,
|
||||
},
|
||||
{ cause: error },
|
||||
),
|
||||
);
|
||||
return channelData;
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
if (forceUpdate) {
|
||||
@@ -43,8 +65,6 @@ export const createCachedRequestHandler = <TData, TInput extends Record<string,
|
||||
return await requestNewDataAsync();
|
||||
}
|
||||
|
||||
const channelData = await channel.getAsync();
|
||||
|
||||
const shouldRequestNewData =
|
||||
!channelData ||
|
||||
dayjs().diff(channelData.timestamp, "milliseconds") > options.cacheDuration.asMilliseconds();
|
||||
|
||||
@@ -111,6 +111,24 @@ describe("getAvailableUpdatesAsync", () => {
|
||||
// Assert
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test("should propagate GitHub API auth errors without blocking callers", async () => {
|
||||
const currentVersion = "v1.2.3";
|
||||
const listReleasesSpy = vi.spyOn(new Octokit().rest.repos, "listReleases");
|
||||
listReleasesSpy.mockRejectedValue(new Error("Bad credentials"));
|
||||
|
||||
await expect(getAvailableUpdatesAsync(currentVersion)).rejects.toThrow("Bad credentials");
|
||||
});
|
||||
|
||||
test("should propagate GitHub API quota exhausted errors without blocking callers", async () => {
|
||||
const currentVersion = "v1.2.3";
|
||||
const listReleasesSpy = vi.spyOn(new Octokit().rest.repos, "listReleases");
|
||||
listReleasesSpy.mockRejectedValue(
|
||||
new Error("Request quota exhausted for request GET /repos/{owner}/{repo}/releases"),
|
||||
);
|
||||
|
||||
await expect(getAvailableUpdatesAsync(currentVersion)).rejects.toThrow("quota exhausted");
|
||||
});
|
||||
});
|
||||
|
||||
const fakeReleases = (...inputs: [string, boolean][]) => inputs.map(fakeRelease);
|
||||
|
||||
@@ -15,7 +15,8 @@ const logger = createLogger({ module: "updateCheckerRequestHandler" });
|
||||
|
||||
export const updateCheckerRequestHandler = createCachedRequestHandler({
|
||||
queryKey: "homarr-update-checker",
|
||||
cacheDuration: dayjs.duration(1, "hour"),
|
||||
cacheDuration: dayjs.duration(1, "day"),
|
||||
fallbackToStaleOnError: true,
|
||||
async requestAsync(_) {
|
||||
return {
|
||||
availableUpdates: await getAvailableUpdatesAsync(packageJson.version),
|
||||
@@ -58,6 +59,7 @@ export const getAvailableUpdatesAsync = async (currentVersion: string) => {
|
||||
request: {
|
||||
fetch: fetchWithTrustedCertificatesAsync,
|
||||
},
|
||||
throttle: { enabled: false },
|
||||
});
|
||||
|
||||
const isCurrentPrerelease = isPrereleaseTag(currentVersion);
|
||||
@@ -100,7 +102,7 @@ export const getAvailableUpdatesAsync = async (currentVersion: string) => {
|
||||
const availableUpdates = semanticReleases
|
||||
.filter((release) => isCurrentPrerelease || !release.isPrerelease)
|
||||
.filter((release) => compareSemVer(release.tagName, currentVersion) > 0)
|
||||
.sort((releaseA, releaseB) => compareSemVer(releaseB.tagName, releaseA.tagName));
|
||||
.toSorted((releaseA, releaseB) => compareSemVer(releaseB.tagName, releaseA.tagName));
|
||||
|
||||
if (availableUpdates.length === 0) {
|
||||
logger.debug("No available updates found", { currentVersion });
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": ""
|
||||
"changePosition": "",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "打开媒体"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "查阅文档",
|
||||
"checkLogs": "检查日志获取更多详情",
|
||||
"tryAgain": "重试",
|
||||
"loading": "正在加载"
|
||||
"loading": "正在加载",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "这里",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "新建分类",
|
||||
"changePosition": "换位"
|
||||
"changePosition": "换位",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "此面板是公开的",
|
||||
"private": "此面板是私有的"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "crwdns2680:0crwdne2680:0"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": "crwdns4251:0crwdne4251:0"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "crwdns546:0crwdne546:0",
|
||||
"checkLogs": "crwdns2260:0crwdne2260:0",
|
||||
"tryAgain": "crwdns548:0crwdne548:0",
|
||||
"loading": "crwdns550:0crwdne550:0"
|
||||
"loading": "crwdns550:0crwdne550:0",
|
||||
"show": "crwdns4255:0crwdne4255:0",
|
||||
"hide": "crwdns4257:0crwdne4257:0"
|
||||
},
|
||||
"here": "crwdns552:0crwdne552:0",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "crwdns686:0crwdne686:0",
|
||||
"changePosition": "crwdns688:0crwdne688:0"
|
||||
"changePosition": "crwdns688:0crwdne688:0",
|
||||
"settings": "crwdns4259:0crwdne4259:0"
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "crwdns1656:0crwdne1656:0",
|
||||
"private": "crwdns1658:0crwdne1658:0"
|
||||
},
|
||||
"noResults": {
|
||||
"title": "crwdns4253:0crwdne4253:0"
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Přečtěte si dokumentaci",
|
||||
"checkLogs": "Pro více podrobností zkontrolujte logy",
|
||||
"tryAgain": "Zkusit znovu",
|
||||
"loading": "Načítání"
|
||||
"loading": "Načítání",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "zde",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nová kategorie",
|
||||
"changePosition": "Změnit pozici"
|
||||
"changePosition": "Změnit pozici",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Åben medie"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Tjek dokumentationen",
|
||||
"checkLogs": "Tjek logs for flere detaljer",
|
||||
"tryAgain": "Prøv igen",
|
||||
"loading": "Indlæser"
|
||||
"loading": "Indlæser",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "her",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Ny kategori",
|
||||
"changePosition": "Ændre placering"
|
||||
"changePosition": "Ændre placering",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Denne tavle er offentlig",
|
||||
"private": "Denne tavle er privat"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Medium öffnen"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Die Dokumentation ansehen",
|
||||
"checkLogs": "Logs für weitere Details prüfen",
|
||||
"tryAgain": "Erneut versuchen",
|
||||
"loading": "Wird geladen"
|
||||
"loading": "Wird geladen",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "Hier",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Neue Kategorie",
|
||||
"changePosition": "Position wechseln"
|
||||
"changePosition": "Position wechseln",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Dieses Board ist öffentlich",
|
||||
"private": "Dieses Board ist privat"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Medium öffnen"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Die Dokumentation ansehen",
|
||||
"checkLogs": "Logs für weitere Details prüfen",
|
||||
"tryAgain": "Erneut versuchen",
|
||||
"loading": "Wird geladen"
|
||||
"loading": "Wird geladen",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "Hier",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Neue Kategorie",
|
||||
"changePosition": "Position wechseln"
|
||||
"changePosition": "Position wechseln",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Dieses Board ist öffentlich",
|
||||
"private": "Dieses Board ist privat"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Προσπαθήστε ξανά",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Αλλαγή θέσης"
|
||||
"changePosition": "Αλλαγή θέσης",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Open media"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Check the documentation",
|
||||
"checkLogs": "Check logs for more information",
|
||||
"tryAgain": "Try again",
|
||||
"loading": "Loading"
|
||||
"loading": "Loading",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "here",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "New category",
|
||||
"changePosition": "Change position"
|
||||
"changePosition": "Change position",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Abrir imagen"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Consultar la documentación",
|
||||
"checkLogs": "Comprueba los registros para más detalles",
|
||||
"tryAgain": "Intentar de nuevo",
|
||||
"loading": "Cargando"
|
||||
"loading": "Cargando",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "aquí",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nueva Categoría",
|
||||
"changePosition": "Cambiar posición"
|
||||
"changePosition": "Cambiar posición",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Este tablero es público",
|
||||
"private": "Este tablero es privado"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": ""
|
||||
"changePosition": "",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Ouvrir le média"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Voir la documentation",
|
||||
"checkLogs": "Vérifiez les logs pour plus de détails",
|
||||
"tryAgain": "Réessayer",
|
||||
"loading": "Chargement"
|
||||
"loading": "Chargement",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "ici",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nouvelle catégorie",
|
||||
"changePosition": "Modifier la position"
|
||||
"changePosition": "Modifier la position",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Ce tableau est public",
|
||||
"private": "Ce tableau est privé"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "פתיחת מדיה"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "בדוק את התיעוד",
|
||||
"checkLogs": "בדוק לוגים לפרטים נוספים",
|
||||
"tryAgain": "נא לנסות שוב",
|
||||
"loading": "טוען"
|
||||
"loading": "טוען",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "כאן",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "קטגוריה חדשה",
|
||||
"changePosition": "שנה מיקום"
|
||||
"changePosition": "שנה מיקום",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "הלוח הזה ציבורי",
|
||||
"private": "הלוח הזה פרטי"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Pokušaj ponovno",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Promijenjen položaj"
|
||||
"changePosition": "Promijenjen položaj",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Média megnyitása"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Tekintse meg a dokumentációt",
|
||||
"checkLogs": "További részletekért ellenőrizze a naplókat",
|
||||
"tryAgain": "Próbálja újra",
|
||||
"loading": "Töltés"
|
||||
"loading": "Töltés",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "itt",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Új kategória",
|
||||
"changePosition": "Pozíció módosítása"
|
||||
"changePosition": "Pozíció módosítása",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Apri media"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": "Nessun media caricato"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Consulta la documentazione",
|
||||
"checkLogs": "Controlla i log per maggiori dettagli",
|
||||
"tryAgain": "Riprova",
|
||||
"loading": "Caricamento"
|
||||
"loading": "Caricamento",
|
||||
"show": "Visualizza",
|
||||
"hide": "Nascondi"
|
||||
},
|
||||
"here": "qui",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nuova categoria",
|
||||
"changePosition": "Cambia posizione"
|
||||
"changePosition": "Cambia posizione",
|
||||
"settings": "Impostazioni"
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Questa board è pubblica",
|
||||
"private": "Questa board è privata"
|
||||
},
|
||||
"noResults": {
|
||||
"title": "Ancora nessuna board"
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "メディアを開く"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "ドキュメントを確認する",
|
||||
"checkLogs": "詳細はログを確認してください",
|
||||
"tryAgain": "リトライ",
|
||||
"loading": "読み込み中"
|
||||
"loading": "読み込み中",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "こちら",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "新しいカテゴリー",
|
||||
"changePosition": "ポジションを変更する"
|
||||
"changePosition": "ポジションを変更する",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "このボードは公開中です",
|
||||
"private": "このボードは非公開です"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "다시 시도",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "위치 변경"
|
||||
"changePosition": "위치 변경",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": ""
|
||||
"changePosition": "",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Mēģiniet vēlreiz",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Mainīt pozīciju"
|
||||
"changePosition": "Mainīt pozīciju",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Media openen"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "De documentatie bekijken",
|
||||
"checkLogs": "Controleer logs voor meer details",
|
||||
"tryAgain": "Probeer opnieuw",
|
||||
"loading": "Laden"
|
||||
"loading": "Laden",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "hier",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nieuwe categorie",
|
||||
"changePosition": "Positie wijzigen"
|
||||
"changePosition": "Positie wijzigen",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Dit bord is openbaar",
|
||||
"private": "Dit bord is privé"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Åpne medie"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Les dokumentasjonen",
|
||||
"checkLogs": "Sjekk loggene for mer informasjon",
|
||||
"tryAgain": "Prøv igjen",
|
||||
"loading": "Laster"
|
||||
"loading": "Laster",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "her",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Ny kategori",
|
||||
"changePosition": "Endre posisjon"
|
||||
"changePosition": "Endre posisjon",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Denne tavlen er offentlig",
|
||||
"private": "Denne tavlen er privat"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Otwórz media"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Sprawdź dokumentację",
|
||||
"checkLogs": "Sprawdź logi, aby uzyskać więcej informacji.",
|
||||
"tryAgain": "Spróbuj ponownie",
|
||||
"loading": "Ładowanie"
|
||||
"loading": "Ładowanie",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "tutaj",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nowa kategoria",
|
||||
"changePosition": "Zmiana pozycji"
|
||||
"changePosition": "Zmiana pozycji",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Ta tablica jest publiczna",
|
||||
"private": "Ta tablica jest prywatna"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Abrir mídia"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Confira a documentação",
|
||||
"checkLogs": "Verifique os logs para mais detalhes",
|
||||
"tryAgain": "Tente novamente",
|
||||
"loading": "Carregando"
|
||||
"loading": "Carregando",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "aqui",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nova categoria",
|
||||
"changePosition": "Mudar de posição"
|
||||
"changePosition": "Mudar de posição",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Este quadro é publico",
|
||||
"private": "Este quadro é privado"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": ""
|
||||
"changePosition": "",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Încearcă din nou",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Schimbă locația"
|
||||
"changePosition": "Schimbă locația",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Открыть медиа"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Посмотреть документацию",
|
||||
"checkLogs": "Проверить логи для подробностей",
|
||||
"tryAgain": "Попробовать снова",
|
||||
"loading": "Загрузка"
|
||||
"loading": "Загрузка",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "здесь",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Новая категория",
|
||||
"changePosition": "Изменить позицию"
|
||||
"changePosition": "Изменить позицию",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Эта панель публичная",
|
||||
"private": "Эта панель приватная"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Otvoriť médiá"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Pozrite si dokumentáciu",
|
||||
"checkLogs": "Ďalšie podrobnosti nájdete v protokoloch",
|
||||
"tryAgain": "Skúste znova",
|
||||
"loading": "Nahrávam"
|
||||
"loading": "Nahrávam",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "tu",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Nová kategória",
|
||||
"changePosition": "Zmeniť pozíciu"
|
||||
"changePosition": "Zmeniť pozíciu",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Táto tabuľa je verejná",
|
||||
"private": "Táto tabuľa je súkromná"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Poskusite znova",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Spremeni položaj"
|
||||
"changePosition": "Spremeni položaj",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"form": {
|
||||
"name": {
|
||||
"label": "Gruppens namn",
|
||||
"description": ""
|
||||
"description": "Namnet måste matcha admin-grupp i extern tjänst"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -159,7 +159,7 @@
|
||||
"label": "Visa sökresultat i ny flik"
|
||||
},
|
||||
"ddgBangs": {
|
||||
"label": ""
|
||||
"label": "Aktivera DuckDuckGo bangs"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -252,10 +252,10 @@
|
||||
"changeDdgBangs": {
|
||||
"notification": {
|
||||
"success": {
|
||||
"message": ""
|
||||
"message": "DuckDuckGo bangs inställningen ändrad"
|
||||
},
|
||||
"error": {
|
||||
"message": ""
|
||||
"message": "DuckDuckGo bangs inställningen kunde inte ändras"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -350,12 +350,12 @@
|
||||
"description": "Tillåt medlemmar att addera valfri applikation på sina tavlor"
|
||||
},
|
||||
"modify-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Modifiera alla appar",
|
||||
"description": "Tillåt att medlemmar modifierar alla appar"
|
||||
},
|
||||
"full-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Fullständiga rättigheter till appar",
|
||||
"description": "Tillåt att medlemmar hanterar, använder och raderar alla appar"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -363,20 +363,20 @@
|
||||
"title": "Tavlor",
|
||||
"item": {
|
||||
"create": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Skapa tavlor",
|
||||
"description": "Tillåt att medlemmar skapar tavlor"
|
||||
},
|
||||
"view-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Se alla tavlor",
|
||||
"description": "Tillåt att medlemmar ser alla tavlor"
|
||||
},
|
||||
"modify-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Modifiera alla tavlor",
|
||||
"description": "Tillåt att medlemmar modifierar alla tavlor (Detta inkluderar inte åtkomstkontroll eller farozonen)"
|
||||
},
|
||||
"full-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Fullständiga rättigheter till tavlor",
|
||||
"description": "Tillåt att medlemmar ser, modifierar och tar bort alla tavlor (detta inkluderar åtkomstkontroll och farozonen)"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -392,38 +392,38 @@
|
||||
"description": "Tillåter medlemmar att addera valfri integration till sina tavlor"
|
||||
},
|
||||
"interact-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Interagera med alla integrationer",
|
||||
"description": "Tillåt att medlemmar interagerar med alla integrationer"
|
||||
},
|
||||
"full-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Fullständiga rättigheter till integrationer",
|
||||
"description": "Tillåt att medlemmar hanterar, använder och interagerar med alla integrationer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"media": {
|
||||
"title": "",
|
||||
"title": "Media",
|
||||
"item": {
|
||||
"upload": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Ladda upp media",
|
||||
"description": "Tillåt att medlemmar laddar upp media"
|
||||
},
|
||||
"view-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Se all media",
|
||||
"description": "Tillåt att medlemmar ser all media"
|
||||
},
|
||||
"full-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Fullständiga rättigheter till media",
|
||||
"description": "Tillåt att medlemmar hanterar och raderar all media"
|
||||
}
|
||||
}
|
||||
},
|
||||
"other": {
|
||||
"title": "",
|
||||
"title": "Annat",
|
||||
"item": {
|
||||
"view-logs": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Se loggar",
|
||||
"description": "Tillåt att medlemmar ser loggar"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -435,32 +435,32 @@
|
||||
"description": "Tillåt medlemmar att skapa sökmotorer"
|
||||
},
|
||||
"modify-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Modifiera alla sökmotorer",
|
||||
"description": "Tillåt att medlemmar modifierar alla sökmotorer"
|
||||
},
|
||||
"full-all": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Fullständiga rättigheter till sökmotorer",
|
||||
"description": "Tillåt att medlemmar hanterar och raderar alla sökmotorer"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"memberNotice": {
|
||||
"mixed": "",
|
||||
"external": ""
|
||||
"mixed": "Vissa medlemmar är från externa tjänster och kan inte hantera här",
|
||||
"external": "Alla medlemmar är från externa tjänster och kan inte hanteras här"
|
||||
},
|
||||
"reservedNotice": {
|
||||
"message": ""
|
||||
"message": "Denna grupp är reserverad för systemet så vissa åtgärder är begränsade. <checkoutDocs></checkoutDocs>"
|
||||
},
|
||||
"action": {
|
||||
"create": {
|
||||
"label": "Addera en grupp",
|
||||
"notification": {
|
||||
"success": {
|
||||
"message": ""
|
||||
"message": "Gruppen har skapats"
|
||||
},
|
||||
"error": {
|
||||
"message": ""
|
||||
"message": "Gruppen kunde inte skapas"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -470,7 +470,7 @@
|
||||
"confirm": "Är du säker på att du vill överföra ägarskapet för gruppen {name} till {username}?",
|
||||
"notification": {
|
||||
"success": {
|
||||
"message": ""
|
||||
"message": "Överfört grupp {group} till {user}"
|
||||
},
|
||||
"error": {
|
||||
"message": "Kunde inte överföra ägarskap"
|
||||
@@ -501,38 +501,38 @@
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "Behörigheter sparade",
|
||||
"message": ""
|
||||
"message": "Rättigheterna sparade"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Rättigheterna ej sparade",
|
||||
"message": "Rättigheterna har inte sparats"
|
||||
}
|
||||
}
|
||||
},
|
||||
"update": {
|
||||
"notification": {
|
||||
"success": {
|
||||
"message": ""
|
||||
"message": "Gruppen {name} har sparats"
|
||||
},
|
||||
"error": {
|
||||
"message": ""
|
||||
"message": "Kunde inte spara gruppen {name}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"label": "Välj en grupp",
|
||||
"notFound": ""
|
||||
"notFound": "Ingen grupp hittad"
|
||||
},
|
||||
"settings": {
|
||||
"board": {
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "Inställningarna sparades",
|
||||
"message": ""
|
||||
"message": "Inställningarna för tavlan har sparats"
|
||||
},
|
||||
"error": {
|
||||
"title": "Kunde inte spara inställningarna",
|
||||
"message": ""
|
||||
"message": "Kunde inte spara inställningarna för tavlan"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,10 +540,10 @@
|
||||
"changePosition": {
|
||||
"notification": {
|
||||
"success": {
|
||||
"message": ""
|
||||
"message": "Ordning ändrad"
|
||||
},
|
||||
"error": {
|
||||
"message": ""
|
||||
"message": "Ordningen kunde ändras"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -559,20 +559,20 @@
|
||||
"list": {
|
||||
"title": "Applikationer",
|
||||
"noResults": {
|
||||
"title": "",
|
||||
"action": ""
|
||||
"title": "Det finns inga appar ännu",
|
||||
"action": "Skapa din första app"
|
||||
}
|
||||
},
|
||||
"create": {
|
||||
"title": "Addera applikation",
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Skapandet lyckades",
|
||||
"message": "Appen skapades framgångsrikt"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Skapandet misslyckades",
|
||||
"message": "Appen kunde inte skapas"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -581,11 +581,11 @@
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "Ändringarna har sparats",
|
||||
"message": ""
|
||||
"message": "Appen sparades"
|
||||
},
|
||||
"error": {
|
||||
"title": "Misslyckades med att spara ändringarna",
|
||||
"message": ""
|
||||
"message": "Appen kunde inte sparas"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -594,12 +594,12 @@
|
||||
"message": "Är du säker på att du vill ta bort applikationen {name}?",
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Borttagning lyckades",
|
||||
"message": "Appen har tagits bort"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Borttagning misslyckades",
|
||||
"message": "Kunde inte ta bort appen"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -612,21 +612,21 @@
|
||||
"label": "Beskrivning"
|
||||
},
|
||||
"url": {
|
||||
"label": ""
|
||||
"label": "URL"
|
||||
},
|
||||
"useDifferentUrlForPing": {
|
||||
"checkbox": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Använd annan URL för ping",
|
||||
"description": "Användbart om Homarr kan komma åt appen direkt via ett internt host-namn eller nätverk för att undvika bandbreddsanvändning hos internetleverantör"
|
||||
}
|
||||
}
|
||||
},
|
||||
"action": {
|
||||
"select": {
|
||||
"label": "",
|
||||
"notFound": "",
|
||||
"label": "Välj app",
|
||||
"notFound": "Ingen app hittades",
|
||||
"search": "Sök efter en applikation",
|
||||
"noResults": "",
|
||||
"noResults": "Inga resultat",
|
||||
"action": "Välj {app}",
|
||||
"title": "Välj en applikation att lägga till på tavlan"
|
||||
},
|
||||
@@ -642,56 +642,56 @@
|
||||
"page": {
|
||||
"list": {
|
||||
"title": "Integrationer",
|
||||
"search": "",
|
||||
"search": "Sök integrationer",
|
||||
"noResults": {
|
||||
"title": ""
|
||||
"title": "Det finns inga integrationer ännu"
|
||||
}
|
||||
},
|
||||
"create": {
|
||||
"title": "",
|
||||
"title": "Ny {name} integration",
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Skapandet lyckades",
|
||||
"message": "Integrationen har skapats"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Skapandet misslyckades",
|
||||
"message": "Integrationen kunde inte skapas"
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
"option": {
|
||||
"existing": {
|
||||
"title": "",
|
||||
"label": ""
|
||||
"title": "Befintlig",
|
||||
"label": "Välj befintlig app"
|
||||
},
|
||||
"new": {
|
||||
"title": "",
|
||||
"title": "Ny",
|
||||
"url": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "App URL",
|
||||
"description": "URL:en som appen kommer att öppna när den används från tavlan"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"title": "",
|
||||
"title": "Redigera {name} integration",
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "Ändringarna har sparats",
|
||||
"message": ""
|
||||
"message": "Integrationen sparad"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Kunde inte tillämpa ändringar",
|
||||
"message": "Integrationen kunde inte sparas"
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
"action": {
|
||||
"add": "",
|
||||
"remove": "",
|
||||
"select": ""
|
||||
"add": "Länka en app",
|
||||
"remove": "Ta bort länk",
|
||||
"select": "Välj en app att länka till"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -700,12 +700,12 @@
|
||||
"message": "Är du säker på att du vill ta bort integrationen {name}?",
|
||||
"notification": {
|
||||
"success": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Borttagning lyckades",
|
||||
"message": "Integrationen har tagits bort"
|
||||
},
|
||||
"error": {
|
||||
"title": "",
|
||||
"message": ""
|
||||
"title": "Borttagning misslyckades",
|
||||
"message": "Kunde inte ta bort integrationen"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -715,44 +715,44 @@
|
||||
"label": "Namn"
|
||||
},
|
||||
"url": {
|
||||
"label": ""
|
||||
"label": "URL"
|
||||
},
|
||||
"attemptSearchEngineCreation": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Skapa sökmotor",
|
||||
"description": "Integrationen \"{kind}\" kan inte användas med sökmotorer. Markera detta för att automatiskt konfigurera sökmotorn."
|
||||
},
|
||||
"app": {
|
||||
"sectionTitle": ""
|
||||
"sectionTitle": "Länkad app"
|
||||
},
|
||||
"createApp": {
|
||||
"label": "",
|
||||
"description": ""
|
||||
"label": "Skapa app",
|
||||
"description": "Skapa en app med samma namn och ikon som integrationen. Lämna fältet nedan tomt för att skapa en app med integrationens URL."
|
||||
},
|
||||
"appHref": {
|
||||
"placeholder": ""
|
||||
"placeholder": "Egen app-URL"
|
||||
}
|
||||
},
|
||||
"action": {
|
||||
"create": ""
|
||||
"create": "Ny integration"
|
||||
},
|
||||
"testConnection": {
|
||||
"action": {
|
||||
"create": "",
|
||||
"edit": ""
|
||||
"create": "Testa anslutningen och skapa",
|
||||
"edit": "Testa anslutningen och skapa"
|
||||
},
|
||||
"error": {
|
||||
"common": {
|
||||
"cause": {
|
||||
"title": ""
|
||||
"title": "Orsak med mer detaljer"
|
||||
}
|
||||
},
|
||||
"unknown": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
"title": "Okänt fel",
|
||||
"description": "Ett okänt fel inträffade, öppna orsaken nedan för att se mer detaljer"
|
||||
},
|
||||
"parse": {
|
||||
"title": "",
|
||||
"description": ""
|
||||
"title": "Parsningsfel",
|
||||
"description": "Svaret kunde inte analyseras. Verifiera att URL pekar på bas-URL för tjänsten."
|
||||
},
|
||||
"authorization": {
|
||||
"title": "",
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Försök igen",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Ändra position"
|
||||
"changePosition": "Ändra position",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Den här tavlan är offentlig",
|
||||
"private": "Den här tavlan är privat"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Medyayı aç"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Dökümanlara göz at",
|
||||
"checkLogs": "Daha fazla ayrıntı için günlükleri kontrol edin",
|
||||
"tryAgain": "Tekrar Deneyin",
|
||||
"loading": "Yükleniyor"
|
||||
"loading": "Yükleniyor",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "buraya",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Yeni Kategori",
|
||||
"changePosition": "Pozisyonu değiştir"
|
||||
"changePosition": "Pozisyonu değiştir",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Bu panel herkese açıktır",
|
||||
"private": "Bu panel özeldir"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "Відкрити медіа"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "Переглянути документацію",
|
||||
"checkLogs": "Щоб дізнатися більше, перевірте логи",
|
||||
"tryAgain": "Повторіть спробу",
|
||||
"loading": "Завантаження"
|
||||
"loading": "Завантаження",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "тут",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "Нова категорія",
|
||||
"changePosition": "Змінити положення"
|
||||
"changePosition": "Змінити положення",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "Ця дошка є публічною",
|
||||
"private": "Ця дошка є приватною"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "",
|
||||
"checkLogs": "",
|
||||
"tryAgain": "Thử lại",
|
||||
"loading": ""
|
||||
"loading": "",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "",
|
||||
"changePosition": "Đổi vị trí"
|
||||
"changePosition": "Đổi vị trí",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "",
|
||||
"private": ""
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -1054,6 +1054,9 @@
|
||||
"open": {
|
||||
"label": "開啟多媒體"
|
||||
}
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
@@ -1084,7 +1087,9 @@
|
||||
"checkoutDocs": "查閱說明書",
|
||||
"checkLogs": "檢查 Logs 以取得更多詳情",
|
||||
"tryAgain": "重試",
|
||||
"loading": "正在讀取"
|
||||
"loading": "正在讀取",
|
||||
"show": "",
|
||||
"hide": ""
|
||||
},
|
||||
"here": "此處",
|
||||
"iconPicker": {
|
||||
@@ -1249,7 +1254,8 @@
|
||||
"menu": {
|
||||
"label": {
|
||||
"create": "創建分類",
|
||||
"changePosition": "變更位置"
|
||||
"changePosition": "變更位置",
|
||||
"settings": ""
|
||||
}
|
||||
},
|
||||
"openAllInNewTabs": {
|
||||
@@ -3452,6 +3458,9 @@
|
||||
"public": "此面板是公開的",
|
||||
"private": "此面板是私人的"
|
||||
},
|
||||
"noResults": {
|
||||
"title": ""
|
||||
},
|
||||
"modal": {
|
||||
"createBoard": {
|
||||
"field": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -20,6 +20,7 @@ export interface SelectWithCustomItemsProps<TSelectItem extends BaseSelectItem>
|
||||
onBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
onFocus?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
||||
w?: string;
|
||||
withinPortal?: boolean;
|
||||
}
|
||||
|
||||
type Props<TSelectItem extends BaseSelectItem> = SelectWithCustomItemsProps<TSelectItem> & {
|
||||
@@ -35,6 +36,7 @@ export const SelectWithCustomItems = <TSelectItem extends BaseSelectItem>({
|
||||
SelectOption,
|
||||
w,
|
||||
clearable,
|
||||
withinPortal = false,
|
||||
...props
|
||||
}: Props<TSelectItem>) => {
|
||||
const combobox = useCombobox({
|
||||
@@ -71,7 +73,7 @@ export const SelectWithCustomItems = <TSelectItem extends BaseSelectItem>({
|
||||
const _clearable = clearable && Boolean(_value);
|
||||
|
||||
return (
|
||||
<Combobox store={combobox} withinPortal={false} onOptionSubmit={onOptionSubmit}>
|
||||
<Combobox store={combobox} withinPortal={withinPortal} onOptionSubmit={onOptionSubmit}>
|
||||
<Combobox.Target>
|
||||
<InputBase
|
||||
{...props}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rm -rf .turbo node_modules",
|
||||
"format": "oxfmt --check .",
|
||||
"format": "oxfmt .",
|
||||
"lint": "oxlint",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -39,7 +39,7 @@ export const ResourceTable = ({ type, data, isTiny }: ResourceTableProps) => {
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{data
|
||||
.toSorted((itemA, itemB) => {
|
||||
.sort((itemA, itemB) => {
|
||||
const nodeResult = itemA.node.localeCompare(itemB.node);
|
||||
if (nodeResult !== 0) return nodeResult;
|
||||
return itemA.name.localeCompare(itemB.name);
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function MediaServerWidget({
|
||||
const newData = filteredData.concat(
|
||||
data.requests.map((request) => ({ ...request, integrationId: data.integrationId })),
|
||||
);
|
||||
return newData.toSorted((dataA, dataB) => {
|
||||
return newData.sort((dataA, dataB) => {
|
||||
if (dataA.status === dataB.status) {
|
||||
return dataB.createdAt.getTime() - dataA.createdAt.getTime();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const useLiveFeedEntries = (input: RouterInputs["widget"]["rssFeed"]["getFeeds"]
|
||||
return oldData
|
||||
?.filter((entry) => entry.feedUrl !== updatedData.url)
|
||||
.concat(updatedData.entries)
|
||||
.toSorted((entryA, entryB) => {
|
||||
.sort((entryA, entryB) => {
|
||||
return entryA.published && entryB.published
|
||||
? new Date(entryB.published).getTime() - new Date(entryA.published).getTime()
|
||||
: 0;
|
||||
|
||||
Generated
+507
-500
File diff suppressed because it is too large.
Load diff
+31
-31
@@ -6,7 +6,7 @@ catalog:
|
||||
"@auth/core": ^0.41.2
|
||||
"@auth/drizzle-adapter": ^1.11.2
|
||||
"@ctrl/deluge": ^8.0.0
|
||||
"@ctrl/qbittorrent": ^9.12.1
|
||||
"@ctrl/qbittorrent": ^9.12.2
|
||||
"@ctrl/transmission": ^7.4.0
|
||||
"@dnd-kit/core": ^6.3.1
|
||||
"@dnd-kit/modifiers": ^9.0.0
|
||||
@@ -20,17 +20,17 @@ catalog:
|
||||
"@immich/sdk": ^2.7.5
|
||||
"@jellyfin/sdk": ^0.13.0
|
||||
"@kubernetes/client-node": ^1.4.0
|
||||
"@mantine/charts": ^9.2.1
|
||||
"@mantine/colors-generator": ^9.2.1
|
||||
"@mantine/core": ^9.2.1
|
||||
"@mantine/dates": ^9.2.1
|
||||
"@mantine/dropzone": ^9.2.1
|
||||
"@mantine/form": ^9.2.1
|
||||
"@mantine/hooks": ^9.2.1
|
||||
"@mantine/modals": ^9.2.1
|
||||
"@mantine/notifications": ^9.2.1
|
||||
"@mantine/spotlight": ^9.2.1
|
||||
"@mantine/tiptap": ^9.2.1
|
||||
"@mantine/charts": ^9.2.2
|
||||
"@mantine/colors-generator": ^9.2.2
|
||||
"@mantine/core": ^9.2.2
|
||||
"@mantine/dates": ^9.2.2
|
||||
"@mantine/dropzone": ^9.2.2
|
||||
"@mantine/form": ^9.2.2
|
||||
"@mantine/hooks": ^9.2.2
|
||||
"@mantine/modals": ^9.2.2
|
||||
"@mantine/notifications": ^9.2.2
|
||||
"@mantine/spotlight": ^9.2.2
|
||||
"@mantine/tiptap": ^9.2.2
|
||||
"@next/eslint-plugin-next": 16.2.6
|
||||
"@octokit/auth-app": ^8.2.0
|
||||
"@paralleldrive/cuid2": ^3.3.0
|
||||
@@ -43,12 +43,12 @@ catalog:
|
||||
"@semantic-release/release-notes-generator": ^14.1.1
|
||||
"@t3-oss/env-nextjs": ^0.13.11
|
||||
"@tabler/icons-react": ^3.44.0
|
||||
"@tanstack/react-query": ^5.100.13
|
||||
"@tanstack/react-query-devtools": ^5.100.13
|
||||
"@tanstack/react-query-next-experimental": ^5.100.13
|
||||
"@testcontainers/mysql": ^12.0.0
|
||||
"@testcontainers/postgresql": ^12.0.0
|
||||
"@testcontainers/redis": ^12.0.0
|
||||
"@tanstack/react-query": ^5.100.14
|
||||
"@tanstack/react-query-devtools": ^5.100.14
|
||||
"@tanstack/react-query-next-experimental": ^5.100.14
|
||||
"@testcontainers/mysql": ^12.0.1
|
||||
"@testcontainers/postgresql": ^12.0.1
|
||||
"@testcontainers/redis": ^12.0.1
|
||||
"@tiptap/extension-color": 3.23.6
|
||||
"@tiptap/extension-highlight": 3.23.6
|
||||
"@tiptap/extension-image": 3.23.6
|
||||
@@ -70,7 +70,7 @@ catalog:
|
||||
"@trpc/react-query": ^11.17.0
|
||||
"@trpc/server": ^11.17.0
|
||||
"@trpc/tanstack-react-query": ^11.17.0
|
||||
"@turbo/gen": ^2.9.14
|
||||
"@turbo/gen": ^2.9.16
|
||||
"@types/adm-zip": 0.5.8
|
||||
"@types/bcrypt": 6.0.0
|
||||
"@types/better-sqlite3": 7.6.13
|
||||
@@ -109,7 +109,7 @@ catalog:
|
||||
croner: 10.0.1
|
||||
cross-env: ^10.1.0
|
||||
crypto-js: ^4.2.0
|
||||
dayjs: ^1.11.20
|
||||
dayjs: ^1.11.21
|
||||
deepmerge: 4.3.1
|
||||
dns-caching: ^0.2.9
|
||||
dockerode: ^5.0.0
|
||||
@@ -124,30 +124,30 @@ catalog:
|
||||
flag-icons: ^7.5.0
|
||||
glob: ^13.0.6
|
||||
ical.js: ^2.2.1
|
||||
ioredis: 5.10.1
|
||||
isomorphic-dompurify: ^3.14.0
|
||||
ioredis: 5.11.0
|
||||
isomorphic-dompurify: ^3.15.0
|
||||
jotai: ^2.20.0
|
||||
jsdom: ^29.1.1
|
||||
json5: ^2.2.3
|
||||
ldapts: 8.1.8
|
||||
lodash.clonedeep: ^4.5.0
|
||||
mantine-react-table: 2.0.0-beta.9
|
||||
mantine-react-table: npm:mantine-react-table-open@9.0.3
|
||||
maria2: ^0.4.1
|
||||
mysql2: 3.22.3
|
||||
mysql2: 3.22.4
|
||||
next: 16.2.6
|
||||
next-auth: 5.0.0-beta.31
|
||||
next-intl: 4.12.0
|
||||
next-intl: 4.13.0
|
||||
node-loader: ^2.1.0
|
||||
octokit: ^5.0.5
|
||||
oxfmt: ^0.51.0
|
||||
oxlint: ^1.66.0
|
||||
oxfmt: ^0.52.0
|
||||
oxlint: ^1.67.0
|
||||
pg: ^8.21.0
|
||||
postcss-preset-mantine: ^1.18.0
|
||||
prismjs: ^1.30.0
|
||||
proxmox-api: 1.1.1
|
||||
react: 19.2.6
|
||||
react-dom: 19.2.6
|
||||
react-error-boundary: ^6.1.1
|
||||
react-error-boundary: ^6.1.2
|
||||
react-markdown: ^10.1.0
|
||||
react-quill-new: ^3.8.3
|
||||
react-simple-code-editor: ^0.14.1
|
||||
@@ -158,12 +158,12 @@ catalog:
|
||||
superjson: 2.2.6
|
||||
svgson: ^5.3.1
|
||||
swagger-ui-react: ^5.32.6
|
||||
testcontainers: ^12.0.0
|
||||
testcontainers: ^12.0.1
|
||||
timezones-ical-library: ^2.2.0
|
||||
trpc-to-openapi: ^3.2.0
|
||||
trpc-to-openapi: ^3.3.0
|
||||
tsdav: ^2.2.2
|
||||
tsx: 4.20.4
|
||||
turbo: ^2.9.14
|
||||
turbo: ^2.9.16
|
||||
typescript: ^5.9.3
|
||||
undici: 7.24.6
|
||||
use-deep-compare-effect: ^1.8.1
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontent.com/u/47495914?v=4"},{"login":"bebsworthy","avatar_url":"https://avatars.githubusercontent.cLine truncated
|
||||
[{"login":"ajnart","avatar_url":"https://avatars.githubusercontent.com/u/49837342?v=4"},{"login":"manuel-rw","avatar_url":"https://avatars.githubusercontent.com/u/30572287?v=4"},{"login":"Meierschlumpf","avatar_url":"https://avatars.githubusercontent.com/u/63781622?v=4"},{"login":"SeDemal","avatar_url":"https://avatars.githubusercontent.com/u/26098587?v=4"},{"login":"lammersbjorn","avatar_url":"https://avatars.githubusercontent.com/u/71191962?v=4"},{"login":"LarveyOfficial","avatar_url":"https://avatars.githubusercontent.com/u/39219859?v=4"},{"login":"c00ldude1oo","avatar_url":"https://avatars.githubusercontent.com/u/15677803?v=4"},{"login":"InterN0te","avatar_url":"https://avatars.githubusercontent.com/u/10882916?v=4"},{"login":"Aimsucks","avatar_url":"https://avatars.githubusercontent.com/u/1828486?v=4"},{"login":"JannesV","avatar_url":"https://avatars.githubusercontent.com/u/5984244?v=4"},{"login":"MauriceNino","avatar_url":"https://avatars.githubusercontent.com/u/18658092?v=4"},{"login":"hillaliy","avatar_url":"https://avatars.githubusercontent.com/u/950010?v=4"},{"login":"Skeeww","avatar_url":"https://avatars.githubusercontent.com/u/9639992?v=4"},{"login":"RichyHBM","avatar_url":"https://avatars.githubusercontent.com/u/5472275?v=4"},{"login":"Bonfire","avatar_url":"https://avatars.githubusercontent.com/u/5704760?v=4"},{"login":"wieli99","avatar_url":"https://avatars.githubusercontent.com/u/46794825?v=4"},{"login":"ishaanparlikar","avatar_url":"https://avatars.githubusercontent.com/u/43013132?v=4"},{"login":"Zareix","avatar_url":"https://avatars.githubusercontent.com/u/29818713?v=4"},{"login":"tuggan","avatar_url":"https://avatars.githubusercontent.com/u/4325608?v=4"},{"login":"Spillebulle","avatar_url":"https://avatars.githubusercontent.com/u/46653946?v=4"},{"login":"marektancak0106","avatar_url":"https://avatars.githubusercontent.com/u/25422667?v=4"},{"login":"spkesDE","avatar_url":"https://avatars.githubusercontent.com/u/1682314?v=4"},{"login":"j3lte","avatar_url":"https://avatars.githubusercontent.com/u/2557568?v=4"},{"login":"dslatt","avatar_url":"https://avatars.githubusercontent.com/u/5211001?v=4"},{"login":"Malong11-007","avatar_url":"https://avatars.githubusercontent.com/u/40298510?v=4"},{"login":"StefanB7","avatar_url":"https://avatars.githubusercontent.com/u/22056509?v=4"},{"login":"souravghosh01","avatar_url":"https://avatars.githubusercontent.com/u/37548139?v=4"},{"login":"SR3u","avatar_url":"https://avatars.githubusercontent.com/u/2380522?v=4"},{"login":"Rikpat","avatar_url":"https://avatars.githubusercontent.com/u/33869814?v=4"},{"login":"NormanJS","avatar_url":"https://avatars.githubusercontent.com/u/83409549?v=4"},{"login":"No-Maines-Land","avatar_url":"https://avatars.githubusercontent.com/u/20687725?v=4"},{"login":"stark1tty","avatar_url":"https://avatars.githubusercontent.com/u/69326376?v=4"},{"login":"Momcilo42","avatar_url":"https://avatars.githubusercontent.com/u/10118137?v=4"},{"login":"marius-arch","avatar_url":"https://avatars.githubusercontent.com/u/72494359?v=4"},{"login":"Lumilias","avatar_url":"https://avatars.githubusercontent.com/u/10852161?v=4"},{"login":"Havock94","avatar_url":"https://avatars.githubusercontent.com/u/7635248?v=4"},{"login":"Lollyde","avatar_url":"https://avatars.githubusercontent.com/u/11347524?v=4"},{"login":"krishnamuppaneni","avatar_url":"https://avatars.githubusercontent.com/u/4703366?v=4"},{"login":"devtobi","avatar_url":"https://avatars.githubusercontent.com/u/28538704?v=4"},{"login":"TyxTang","avatar_url":"https://avatars.githubusercontent.com/u/90897955?v=4"},{"login":"alotlikebeans","avatar_url":"https://avatars.githubusercontent.com/u/19630511?v=4"},{"login":"WillyJL","avatar_url":"https://avatars.githubusercontent.com/u/49810075?v=4"},{"login":"anonysoul","avatar_url":"https://avatars.githubusercontent.com/u/105715150?v=4"},{"login":"cospeedster","avatar_url":"https://avatars.githubusercontent.com/u/52749645?v=4"},{"login":"diederbert","avatar_url":"https://avatars.githubusercontent.com/u/162878798?v=4"},{"login":"digdilem","avatar_url":"https://avatars.githubusercontent.com/u/4052275?v=4"},{"login":"dinghino","avatar_url":"https://avatars.githubusercontent.com/u/14729459?v=4"},{"login":"gfomichev","avatar_url":"https://avatars.githubusercontent.com/u/1596146?v=4"},{"login":"gnattu","avatar_url":"https://avatars.githubusercontent.com/u/12995396?v=4"},{"login":"tuxpizza","avatar_url":"https://avatars.githubusercontent.com/u/84710786?v=4"},{"login":"xmarkurion","avatar_url":"https://avatars.githubusercontent.com/u/46654687?v=4"},{"login":"huanga","avatar_url":"https://avatars.githubusercontent.com/u/732970?v=4"},{"login":"taos15","avatar_url":"https://avatars.githubusercontent.com/u/122130728?v=4"},{"login":"arghyadipchak","avatar_url":"https://avatars.githubusercontent.com/u/32430374?v=4"},{"login":"AuthorShin","avatar_url":"https://avatars.githubusercontent.com/u/4959043?v=4"},{"login":"benniblot","avatar_url":"https://avatars.githubusercontLine truncated
|
||||
@@ -1 +1 @@
|
||||
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"httpsLine truncated
|
||||
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/249b02e3368752cb175446082015737c_default.png"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"httpsLine truncated
|
||||
Reference in new issue
Block a user