mirror of
https://github.com/kyantech/Palmr.git
synced 2025-10-23 06:11:58 +00:00
refactor(settings): reorganize imports and improve code readability
Restructured imports across multiple files to follow a consistent order and improve readability. Also, adjusted some code formatting for better maintainability.
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useAppInfo } from "@/contexts/app-info-context";
|
|
||||||
import { removeLogo, uploadLogo } from "@/http/endpoints";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { IconCloudUpload, IconTrash } from "@tabler/icons-react";
|
import { IconCloudUpload, IconTrash } from "@tabler/icons-react";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useAppInfo } from "@/contexts/app-info-context";
|
||||||
|
import { removeLogo, uploadLogo } from "@/http/endpoints";
|
||||||
|
|
||||||
interface LogoInputProps {
|
interface LogoInputProps {
|
||||||
value?: string;
|
value?: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
@@ -15,7 +16,7 @@ interface LogoInputProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function LogoInput({ value, onChange, isDisabled }: LogoInputProps) {
|
export function LogoInput({ value, onChange, isDisabled }: LogoInputProps) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const [isUploading, setIsUploading] = useState(false);
|
const [isUploading, setIsUploading] = useState(false);
|
||||||
const [currentLogo, setCurrentLogo] = useState(value);
|
const [currentLogo, setCurrentLogo] = useState(value);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -80,19 +81,9 @@ export function LogoInput({ value, onChange, isDisabled }: LogoInputProps) {
|
|||||||
{currentLogo ? (
|
{currentLogo ? (
|
||||||
<div className="flex flex-col items-center gap-4">
|
<div className="flex flex-col items-center gap-4">
|
||||||
<div className="relative max-w-[200px] max-h-[200px] flex">
|
<div className="relative max-w-[200px] max-h-[200px] flex">
|
||||||
<img
|
<img alt={t("logo.labels.appLogo")} className="rounded-lg" src={currentLogo} sizes="200px" />
|
||||||
alt={t("logo.labels.appLogo")}
|
|
||||||
className="rounded-lg"
|
|
||||||
src={currentLogo}
|
|
||||||
|
|
||||||
sizes="200px"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button variant="destructive" disabled={isDisabled} onClick={handleRemoveLogo}>
|
||||||
variant="destructive"
|
|
||||||
disabled={isDisabled}
|
|
||||||
onClick={handleRemoveLogo}
|
|
||||||
>
|
|
||||||
{!isUploading && <IconTrash className="mr-2 h-4 w-4" />}
|
{!isUploading && <IconTrash className="mr-2 h-4 w-4" />}
|
||||||
{t("logo.buttons.remove")}
|
{t("logo.buttons.remove")}
|
||||||
</Button>
|
</Button>
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { ValidGroup } from "../types";
|
import { SettingsFormProps, ValidGroup } from "../types";
|
||||||
import { SettingsFormProps } from "../types";
|
|
||||||
import { SettingsGroup } from "./settings-group";
|
import { SettingsGroup } from "./settings-group";
|
||||||
|
|
||||||
const GROUP_ORDER: ValidGroup[] = ["general", "email", "security", "storage"];
|
const GROUP_ORDER: ValidGroup[] = ["general", "email", "security", "storage"];
|
||||||
|
@@ -1,12 +1,13 @@
|
|||||||
import { createGroupMetadata, createFieldDescriptions } from "../constants";
|
import React from "react";
|
||||||
import { SettingsGroupProps } from "../types";
|
import { IconChevronDown, IconChevronUp, IconDeviceFloppy } from "@tabler/icons-react";
|
||||||
import { SettingsInput } from "./settings-input";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { useTranslations } from "next-intl";
|
import { createFieldDescriptions, createGroupMetadata } from "../constants";
|
||||||
import React from "react";
|
import { SettingsGroupProps } from "../types";
|
||||||
import { IconChevronDown, IconChevronUp, IconDeviceFloppy } from "@tabler/icons-react";
|
import { SettingsInput } from "./settings-input";
|
||||||
|
|
||||||
export function SettingsGroup({ group, configs, form, isCollapsed, onToggleCollapse, onSubmit }: SettingsGroupProps) {
|
export function SettingsGroup({ group, configs, form, isCollapsed, onToggleCollapse, onSubmit }: SettingsGroupProps) {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
@@ -21,7 +22,10 @@ export function SettingsGroup({ group, configs, form, isCollapsed, onToggleColla
|
|||||||
return (
|
return (
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<Card className="p-6 gap-0">
|
<Card className="p-6 gap-0">
|
||||||
<CardHeader className="flex flex-row items-center justify-between cursor-pointer p-0" onClick={onToggleCollapse}>
|
<CardHeader
|
||||||
|
className="flex flex-row items-center justify-between cursor-pointer p-0"
|
||||||
|
onClick={onToggleCollapse}
|
||||||
|
>
|
||||||
<div className="flex flex-row items-center gap-8">
|
<div className="flex flex-row items-center gap-8">
|
||||||
{metadata.icon && React.createElement(metadata.icon, { className: "text-xl text-muted-foreground" })}
|
{metadata.icon && React.createElement(metadata.icon, { className: "text-xl text-muted-foreground" })}
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
@@ -33,7 +37,11 @@ export function SettingsGroup({ group, configs, form, isCollapsed, onToggleColla
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isCollapsed ? <IconChevronDown className="text-muted-foreground" /> : <IconChevronUp className="text-muted-foreground" />}
|
{isCollapsed ? (
|
||||||
|
<IconChevronDown className="text-muted-foreground" />
|
||||||
|
) : (
|
||||||
|
<IconChevronUp className="text-muted-foreground" />
|
||||||
|
)}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className={`${isCollapsed ? "hidden" : "block"} px-0`}>
|
<CardContent className={`${isCollapsed ? "hidden" : "block"} px-0`}>
|
||||||
<Separator className="my-6" />
|
<Separator className="my-6" />
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
import {
|
import Link from "next/link";
|
||||||
Breadcrumb,
|
import { IconLayoutDashboard, IconSettings } from "@tabler/icons-react";
|
||||||
BreadcrumbItem,
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbList,
|
BreadcrumbList,
|
||||||
BreadcrumbSeparator
|
BreadcrumbSeparator,
|
||||||
} from "@/components/ui/breadcrumb";
|
} from "@/components/ui/breadcrumb";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
import { IconSettings } from "@tabler/icons-react";
|
|
||||||
import { IconLayoutDashboard } from "@tabler/icons-react";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
export function SettingsHeader() {
|
export function SettingsHeader() {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import { UseFormRegister, UseFormWatch } from "react-hook-form";
|
||||||
|
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { createFieldTitles } from "../constants";
|
import { createFieldTitles } from "../constants";
|
||||||
import { Config } from "../types";
|
import { Config } from "../types";
|
||||||
import { LogoInput } from "./logo-input";
|
import { LogoInput } from "./logo-input";
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { UseFormRegister, UseFormWatch } from "react-hook-form";
|
|
||||||
|
|
||||||
|
|
||||||
export interface ConfigInputProps {
|
export interface ConfigInputProps {
|
||||||
config: Config;
|
config: Config;
|
||||||
|
@@ -1,25 +1,25 @@
|
|||||||
import { createTranslator } from 'next-intl';
|
import { IconDatabase, IconMail, IconSettings, IconShield } from "@tabler/icons-react";
|
||||||
import { IconMail, IconSettings, IconShield, IconDatabase } from '@tabler/icons-react';
|
import { createTranslator } from "next-intl";
|
||||||
|
|
||||||
export const createGroupMetadata = (t: ReturnType<typeof createTranslator>) => ({
|
export const createGroupMetadata = (t: ReturnType<typeof createTranslator>) => ({
|
||||||
email: {
|
email: {
|
||||||
title: t('settings.groups.email.title'),
|
title: t("settings.groups.email.title"),
|
||||||
description: t('settings.groups.email.description'),
|
description: t("settings.groups.email.description"),
|
||||||
icon: IconMail,
|
icon: IconMail,
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
title: t('settings.groups.general.title'),
|
title: t("settings.groups.general.title"),
|
||||||
description: t('settings.groups.general.description'),
|
description: t("settings.groups.general.description"),
|
||||||
icon: IconSettings,
|
icon: IconSettings,
|
||||||
},
|
},
|
||||||
security: {
|
security: {
|
||||||
title: t('settings.groups.security.title'),
|
title: t("settings.groups.security.title"),
|
||||||
description: t('settings.groups.security.description'),
|
description: t("settings.groups.security.description"),
|
||||||
icon: IconShield,
|
icon: IconShield,
|
||||||
},
|
},
|
||||||
storage: {
|
storage: {
|
||||||
title: t('settings.groups.storage.title'),
|
title: t("settings.groups.storage.title"),
|
||||||
description: t('settings.groups.storage.description'),
|
description: t("settings.groups.storage.description"),
|
||||||
icon: IconDatabase,
|
icon: IconDatabase,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -1,28 +1,25 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ConfigType, GroupFormData } from "../types";
|
|
||||||
import { Config } from "../types";
|
|
||||||
import { useShareContext } from "@/contexts/share-context";
|
|
||||||
import { useAppInfo } from "@/contexts/app-info-context";
|
|
||||||
import { getAllConfigs, bulkUpdateConfigs } from "@/http/endpoints";
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
|
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useTranslations } from "next-intl";
|
|
||||||
|
import { useAppInfo } from "@/contexts/app-info-context";
|
||||||
|
import { useShareContext } from "@/contexts/share-context";
|
||||||
|
import { bulkUpdateConfigs, getAllConfigs } from "@/http/endpoints";
|
||||||
|
import { Config, ConfigType, GroupFormData } from "../types";
|
||||||
|
|
||||||
const createSchemas = () => ({
|
const createSchemas = () => ({
|
||||||
settingsSchema: z.object({
|
settingsSchema: z.object({
|
||||||
configs: z.record(
|
configs: z.record(z.union([z.string(), z.number()]).transform((val) => String(val))),
|
||||||
z.union([z.string(), z.number()]).transform((val) => String(val))
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function useSettings() {
|
export function useSettings() {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const { settingsSchema } = createSchemas();
|
const { settingsSchema } = createSchemas();
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [configs, setConfigs] = useState<Record<string, string>>({});
|
const [configs, setConfigs] = useState<Record<string, string>>({});
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
"use client"
|
"use client";
|
||||||
|
|
||||||
import { ProtectedRoute } from "@/components/auth/protected-route";
|
import { ProtectedRoute } from "@/components/auth/protected-route";
|
||||||
import { LoadingScreen } from "@/components/layout/loading-screen";
|
import { LoadingScreen } from "@/components/layout/loading-screen";
|
||||||
@@ -16,7 +16,7 @@ export default function SettingsPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ProtectedRoute>
|
<ProtectedRoute requireAdmin>
|
||||||
<div className="w-full h-screen flex flex-col">
|
<div className="w-full h-screen flex flex-col">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div className="flex-1 max-w-7xl mx-auto w-full px-6 py-8">
|
<div className="flex-1 max-w-7xl mx-auto w-full px-6 py-8">
|
||||||
|
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
"inline-flex items-center cursor-pointer justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
Reference in New Issue
Block a user