diff --git a/package.json b/package.json index 71c49ec..36a7c4c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "scheduler-ui-by-moda20", + "name": "type-scheduler-ui-by-moda20", "description": "The frontend for the scheduler backend", "private": false, "version": "0.0.0-pre-alpha", @@ -24,19 +24,22 @@ "@icons-pack/react-simple-icons": "^13.8.0", "@melloware/react-logviewer": "^6.3.5", "@monaco-editor/react": "^4.7.0", + "@radix-ui/primitive": "1.1.2", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-checkbox": "^1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-popover": "^1.1.6", + "@radix-ui/react-primitive": "2.1.2", "@radix-ui/react-scroll-area": "^1.2.2", "@radix-ui/react-select": "^2.1.4", "@radix-ui/react-separator": "^1.1.8", - "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-slot": "^1.3.0", "@radix-ui/react-switch": "^1.2.6", "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.2", @@ -47,6 +50,7 @@ "@tanstack/react-query": "^5.90.21", "@tanstack/react-table": "^8.20.5", "@tanstack/react-virtual": "^3.13.18", + "@types/lodash-es": "^4.17.12", "ansi-colors": "^4.1.3", "axios": "^1.10.0", "class-variance-authority": "^0.7.1", @@ -58,6 +62,7 @@ "fuse.js": "^7.4.0", "history": "^5.3.0", "js-cookie": "^3.0.5", + "lodash-es": "^4.18.1", "lucide": "^0.474.0", "lucide-react": "^0.453.0", "moment": "^2.30.1", diff --git a/src/components/custom/DrawerMenuConfigurator.tsx b/src/components/custom/DrawerMenuConfigurator.tsx index a736b3a..9b20465 100644 --- a/src/components/custom/DrawerMenuConfigurator.tsx +++ b/src/components/custom/DrawerMenuConfigurator.tsx @@ -16,6 +16,7 @@ import { verifyUserConnection } from "@/utils/authUtils" import { ButtonWithTooltip } from "@/components/custom/general/ButtonWithTooltip" export default function DrawerMenuConfigurator() { + const [open, setOpen] = useState(false) useHotkeys( ["ctrl+l", "meta+l"], () => { @@ -25,6 +26,7 @@ export default function DrawerMenuConfigurator() { }, { preventDefault: true, + enabled: open, }, ) @@ -69,6 +71,7 @@ export default function DrawerMenuConfigurator() { { enableOnFormTags: true, enableOnContentEditable: true, + enabled: open, }, ) @@ -88,6 +91,7 @@ export default function DrawerMenuConfigurator() { side={"right"} title={"Configuration"} description={"Update server configuration"} + onOpenChange={setOpen} trigger={ + + + + ( +
+ + + Select the Proxies to link to this job + +
+ + + + + The selected proxy will be part of the job's linked + proxies. The proxies are going to be injected to the + job based on a strategy + + +
+
+ )} + /> +
+ + + + + + Update proxy links + + + + + + + ) +} diff --git a/src/components/custom/system/ProxyConfigDialog.tsx b/src/components/custom/system/ProxyConfigDialog.tsx index 5d1cb48..16c4cca 100644 --- a/src/components/custom/system/ProxyConfigDialog.tsx +++ b/src/components/custom/system/ProxyConfigDialog.tsx @@ -27,7 +27,7 @@ import { ComboBox } from "@/components/ui/combo-box" import { cn, parseCron } from "@/lib/utils" import useDialogueManager from "@/hooks/useDialogManager" import { useHotkeys } from "react-hotkeys-hook" -import type { ProxyTableData } from "@/models/proxies" +import { ProxyTableData, ProxyUpdateSchema } from "@/models/proxies" import { proxyProtocolOptions, ProxyStatus } from "@/models/proxies" import { Checkbox } from "@/components/ui/checkbox" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" @@ -35,7 +35,7 @@ import { PasswordInput } from "@/components/ui/password-input" import ManagedSelect from "@/components/custom/ManagedSelect" import ButtonWithStrCut from "@/components/custom/general/ButtonWithStrCut" -export interface ProxyConffigDialogProps { +export interface ProxyConfigDialogProps { children: React.ReactNode isCreateDialog?: boolean proxyDetails?: ProxyTableData @@ -44,27 +44,13 @@ export interface ProxyConffigDialogProps { triggerClassName?: string } -const ProxyUpdateSchema = z.object({ - id: z.union([z.number(), z.string()]).optional(), - proxy_ip: z.string(), - proxy_port: z.coerce.number().positive().max(65535), - description: z.string().optional(), - status: z.union([z.nativeEnum(ProxyStatus), z.number()]).optional(), - username: z.string().optional(), - password: z.string().optional(), - protocol: z.string().optional(), - jobs: z.array(z.number()).optional(), -}) - -export type ProxyConfigUpdateType = z.infer - export function ProxyConfigDialog({ children, isCreateDialog, proxyDetails, onChange, triggerClassName, -}: ProxyConffigDialogProps) { +}: ProxyConfigDialogProps) { const form = useForm>({ resolver: zodResolver(ProxyUpdateSchema), defaultValues: { diff --git a/src/components/ui/input-base.tsx b/src/components/ui/input-base.tsx new file mode 100644 index 0000000..1e3618c --- /dev/null +++ b/src/components/ui/input-base.tsx @@ -0,0 +1,213 @@ +"use client" + +import { composeEventHandlers } from "@radix-ui/primitive" +import { useComposedRefs } from "@radix-ui/react-compose-refs" +import { Primitive } from "@radix-ui/react-primitive" +import { Slot } from "@radix-ui/react-slot" +import * as React from "react" + +import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils" + +export type InputBaseContextProps = Pick< + InputBaseProps, + "autoFocus" | "disabled" +> & { + controlRef: React.RefObject + onFocusedChange: (focused: boolean) => void +} + +const InputBaseContext = React.createContext({ + autoFocus: false, + controlRef: { current: null }, + disabled: false, + onFocusedChange: () => {}, +}) + +function useInputBase() { + const context = React.useContext(InputBaseContext) + if (!context) { + throw new Error("useInputBase must be used within a .") + } + + return context +} + +export interface InputBaseProps + extends React.ComponentProps { + autoFocus?: boolean + disabled?: boolean + error?: boolean +} + +function InputBase({ + autoFocus, + disabled, + className, + onClick, + error, + ...props +}: InputBaseProps) { + const [focused, setFocused] = React.useState(false) + const controlRef = React.useRef(null) + + return ( + + implementation. + // https://github.com/mui/material-ui/blob/master/packages/mui-material/src/InputBase/InputBase.js#L458~L460 + onClick={composeEventHandlers(onClick, event => { + if (controlRef.current && event.currentTarget === event.target) { + controlRef.current.focus() + } + })} + className={cn( + "border-input selection:bg-primary selection:text-primary-foreground dark:bg-input/30 flex min-h-9 cursor-text items-center gap-2 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none md:text-sm", + disabled && "pointer-events-none cursor-not-allowed opacity-50", + focused && "border-ring ring-ring/50 ring-[3px]", + error && + "ring-destructive/20 dark:ring-destructive/40 border-destructive", + className, + )} + {...props} + /> + + ) +} + +function InputBaseFlexWrapper({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function InputBaseControl({ + ref, + onFocus, + onBlur, + ...props +}: React.ComponentProps) { + const { controlRef, autoFocus, disabled, onFocusedChange } = useInputBase() + + const composedRefs = useComposedRefs(controlRef, ref) + + return ( + onFocusedChange(true))} + onBlur={composeEventHandlers(onBlur, () => onFocusedChange(false))} + {...{ disabled }} + {...props} + /> + ) +} + +export interface InputBaseAdornmentProps extends React.ComponentProps<"div"> { + asChild?: boolean +} + +function InputBaseAdornment({ + className, + asChild, + children, + ...props +}: InputBaseAdornmentProps) { + const Comp = asChild ? Slot : typeof children === "string" ? "p" : "div" + + return ( + + {children} + + ) +} + +function InputBaseAdornmentButton({ + type = "button", + variant = "ghost", + size = "icon", + disabled: disabledProp, + className, + ...props +}: React.ComponentProps) { + const { disabled } = useInputBase() + + return ( +