diff --git a/Dockerfile b/Dockerfile
index 3846f42..1da4ae8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,22 +1,7 @@
-FROM node:20-slim AS base
-
-RUN apt-get update && apt-get install -y \
- pciutils \
- curl \
- iputils-ping \
- util-linux \
- ca-certificates \
- gnupg \
- lsb-release \
- && rm -rf /var/lib/apt/lists/*
-
-RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \
- && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \
- && apt-get update \
- && apt-get install -y docker-ce-cli \
- && rm -rf /var/lib/apt/lists/*
+FROM node:20-alpine AS base
FROM base AS deps
+RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
@@ -42,26 +27,27 @@ WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
-RUN groupadd --system --gid 1001 nodejs
-RUN useradd --system --uid 1001 nextjs
+RUN apk add --no-cache su-exec docker-cli pciutils curl iputils util-linux ca-certificates
+
+RUN addgroup --system --gid 1001 nodejs
+RUN adduser --system --uid 1001 nextjs
RUN mkdir -p /app/scripts /app/data /app/snippets && \
chown -R nextjs:nodejs /app/scripts /app/data /app/snippets
-COPY --from=builder /app/public ./public
-
-COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
+RUN mkdir -p /app/.next/cache && \
+ chown -R nextjs:nodejs /app/.next
-COPY --from=builder --chown=nextjs:nodejs /app/app ./app
+COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
-COPY --from=builder /app/package.json ./package.json
-COPY --from=builder /app/yarn.lock ./yarn.lock
-
-COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
+COPY --from=builder /app/public ./public
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
-CMD ["yarn", "start"]
+USER nextjs
+
+CMD ["node", "server.js"]
diff --git a/README.md b/README.md
index 8f35d21..619fa18 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
-
- {job.schedule}
-
- )}
+
+ {job.schedule}
+
+ )}
{scheduleDisplayMode === "human" && cronExplanation?.isValid && (
- {cronExplanation.humanReadable}
@@ -172,7 +171,7 @@ export const CronJobItem = ({ {
@@ -181,7 +180,7 @@ export const CronJobItem = ({
setCommandCopied(job.id);
setTimeout(() => setCommandCopied(null), 3000);
}}
- className="w-full cursor-pointer overflow-x-auto text-sm font-medium text-foreground bg-muted/30 px-2 py-1 rounded border border-border/30 hide-scrollbar"
+ className="w-full cursor-pointer overflow-x-auto text-sm font-medium terminal-font bg-background1 px-2 py-1 ascii-border hide-scrollbar"
>
{unwrapCommand(displayCommand)}
@@ -191,8 +190,8 @@ export const CronJobItem = ({
{cronExplanation.humanReadable}
@@ -201,7 +200,7 @@ export const CronJobItem = ({ {job.comment && ({job.comment} @@ -210,13 +209,13 @@ export const CronJobItem = ({
+
+ ,
+ icon:
+
{job.schedule}
)}
{scheduleDisplayMode === "human" && cronExplanation?.isValid && (
-
-
+
+
{cronExplanation.humanReadable}
@@ -159,15 +159,15 @@ export const MinimalCronJobItem = ({
)}
{scheduleDisplayMode === "both" && (
-
+
{job.schedule}
{cronExplanation?.isValid && (
-
+
)}
@@ -177,7 +177,7 @@ export const MinimalCronJobItem = ({
{commandCopied === job.id && (
-
+
)}
{
@@ -186,7 +186,7 @@ export const MinimalCronJobItem = ({
setCommandCopied(job.id);
setTimeout(() => setCommandCopied(null), 3000);
}}
- className="flex-1 cursor-pointer overflow-hidden text-sm font-medium text-foreground bg-muted/30 px-2 py-1 rounded border border-border/30 truncate"
+ className="flex-1 cursor-pointer overflow-hidden text-sm font-medium terminal-font bg-background1 px-2 py-1 ascii-border truncate"
title={unwrapCommand(job.command)}
>
{unwrapCommand(displayCommand)}
@@ -197,25 +197,25 @@ export const MinimalCronJobItem = ({
{job.logsEnabled && (
)}
{job.paused && (
)}
{!job.logError?.hasError && job.logsEnabled && (
)}
{job.logsEnabled && job.logError?.hasError && (
{
e.stopPropagation();
@@ -225,31 +225,32 @@ export const MinimalCronJobItem = ({
)}
{!job.logsEnabled && errors.length > 0 && (
onErrorClick(errors[0])}
/>
)}
-
+
diff --git a/app/_components/FeatureComponents/Games/SnakeGame.tsx b/app/_components/FeatureComponents/Games/SnakeGame.tsx
new file mode 100644
index 0000000..f994e3b
--- /dev/null
+++ b/app/_components/FeatureComponents/Games/SnakeGame.tsx
@@ -0,0 +1,431 @@
+"use client";
+
+import { useEffect, useRef, useState, useCallback } from "react";
+import { useTranslations } from "next-intl";
+import { ArrowUpIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightIcon, ArrowClockwiseIcon, PlayIcon, PauseIcon } from "@phosphor-icons/react";
+
+interface Position {
+ x: number;
+ y: number;
+}
+
+type Direction = "UP" | "DOWN" | "LEFT" | "RIGHT";
+
+const GRID_SIZE = 20;
+const INITIAL_SNAKE: Position[] = [
+ { x: 10, y: 10 },
+ { x: 9, y: 10 },
+ { x: 8, y: 10 },
+];
+const INITIAL_DIRECTION: Direction = "RIGHT";
+const GAME_SPEED = 150;
+
+export const SnakeGame = () => {
+ const t = useTranslations("notFound");
+ const canvasRef = useRef(null);
+ const containerRef = useRef(null);
+ const [snake, setSnake] = useState(INITIAL_SNAKE);
+ const [direction, setDirection] = useState(INITIAL_DIRECTION);
+ const [food, setFood] = useState({ x: 15, y: 15 });
+ const [gameOver, setGameOver] = useState(false);
+ const [gameStarted, setGameStarted] = useState(false);
+ const [score, setScore] = useState(0);
+ const [highScore, setHighScore] = useState(0);
+ const [isPaused, setIsPaused] = useState(false);
+ const [colors, setColors] = useState({ snake: "#00ff00", food: "#ff0000", grid: "#333333" });
+ const [cellSize, setCellSize] = useState(20);
+
+ const directionRef = useRef(INITIAL_DIRECTION);
+ const gameLoopRef = useRef(null);
+
+ useEffect(() => {
+ const savedHighScore = localStorage.getItem("snakeHighScore");
+ if (savedHighScore) {
+ setHighScore(parseInt(savedHighScore));
+ }
+
+ const updateColors = () => {
+ const theme = document.documentElement.getAttribute("data-webtui-theme");
+ if (theme === "catppuccin-mocha") {
+ setColors({
+ snake: "#9ca0b0",
+ food: "#f38ba8",
+ grid: "#313244",
+ });
+ } else {
+ setColors({
+ snake: "#313244",
+ food: "#d20f39",
+ grid: "#9ca0b0",
+ });
+ }
+ };
+
+ updateColors();
+
+ const observer = new MutationObserver(updateColors);
+ observer.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ["data-webtui-theme"],
+ });
+
+ const updateCellSize = () => {
+ if (containerRef.current) {
+ const containerWidth = containerRef.current.offsetWidth;
+ const maxCanvasSize = Math.min(containerWidth - 32, 400);
+ const newCellSize = Math.floor(maxCanvasSize / GRID_SIZE);
+ setCellSize(newCellSize);
+ }
+ };
+
+ updateCellSize();
+ window.addEventListener("resize", updateCellSize);
+
+ return () => {
+ observer.disconnect();
+ window.removeEventListener("resize", updateCellSize);
+ };
+ }, []);
+
+ const generateFood = useCallback((): Position => {
+ let newFood: Position;
+ do {
+ newFood = {
+ x: Math.floor(Math.random() * GRID_SIZE),
+ y: Math.floor(Math.random() * GRID_SIZE),
+ };
+ } while (snake.some((segment) => segment.x === newFood.x && segment.y === newFood.y));
+ return newFood;
+ }, [snake]);
+
+ const resetGame = useCallback(() => {
+ setSnake(INITIAL_SNAKE);
+ setDirection(INITIAL_DIRECTION);
+ directionRef.current = INITIAL_DIRECTION;
+ setFood(generateFood());
+ setGameOver(false);
+ setGameStarted(true);
+ setScore(0);
+ setIsPaused(false);
+ }, [generateFood]);
+
+ const draw = useCallback(() => {
+ const canvas = canvasRef.current;
+ if (!canvas) return;
+
+ const ctx = canvas.getContext("2d");
+ if (!ctx) return;
+
+ const theme = document.documentElement.getAttribute("data-webtui-theme");
+ const bgColor = theme === "catppuccin-mocha" ? "#1e1e2e" : "#eff1f5";
+
+ ctx.fillStyle = bgColor;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ ctx.strokeStyle = colors.grid;
+ ctx.lineWidth = 1;
+ for (let i = 0; i <= GRID_SIZE; i++) {
+ ctx.beginPath();
+ ctx.moveTo(i * cellSize, 0);
+ ctx.lineTo(i * cellSize, GRID_SIZE * cellSize);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(0, i * cellSize);
+ ctx.lineTo(GRID_SIZE * cellSize, i * cellSize);
+ ctx.stroke();
+ }
+
+ snake.forEach((segment) => {
+ ctx.fillStyle = colors.snake;
+ ctx.fillRect(
+ segment.x * cellSize + 1,
+ segment.y * cellSize + 1,
+ cellSize - 2,
+ cellSize - 2
+ );
+ });
+
+ ctx.fillStyle = colors.food;
+ ctx.fillRect(
+ food.x * cellSize + 1,
+ food.y * cellSize + 1,
+ cellSize - 2,
+ cellSize - 2
+ );
+ }, [snake, food, colors, cellSize]);
+
+ useEffect(() => {
+ draw();
+ }, [draw]);
+
+ const moveSnake = useCallback(() => {
+ if (gameOver || !gameStarted || isPaused) return;
+
+ setSnake((prevSnake) => {
+ const head = prevSnake[0];
+ const newHead: Position = { ...head };
+
+ switch (directionRef.current) {
+ case "UP":
+ newHead.y -= 1;
+ break;
+ case "DOWN":
+ newHead.y += 1;
+ break;
+ case "LEFT":
+ newHead.x -= 1;
+ break;
+ case "RIGHT":
+ newHead.x += 1;
+ break;
+ }
+
+ if (
+ newHead.x < 0 ||
+ newHead.x >= GRID_SIZE ||
+ newHead.y < 0 ||
+ newHead.y >= GRID_SIZE ||
+ prevSnake.some((segment) => segment.x === newHead.x && segment.y === newHead.y)
+ ) {
+ setGameOver(true);
+ setGameStarted(false);
+ return prevSnake;
+ }
+
+ const newSnake = [newHead, ...prevSnake];
+
+ if (newHead.x === food.x && newHead.y === food.y) {
+ setScore((prev) => {
+ const newScore = prev + 10;
+ if (newScore > highScore) {
+ setHighScore(newScore);
+ localStorage.setItem("snakeHighScore", newScore.toString());
+ }
+ return newScore;
+ });
+ setFood(generateFood());
+ } else {
+ newSnake.pop();
+ }
+
+ return newSnake;
+ });
+ }, [gameOver, gameStarted, isPaused, food, highScore, generateFood]);
+
+ useEffect(() => {
+ if (gameStarted && !gameOver && !isPaused) {
+ gameLoopRef.current = setInterval(moveSnake, GAME_SPEED);
+ }
+
+ return () => {
+ if (gameLoopRef.current) {
+ clearInterval(gameLoopRef.current);
+ }
+ };
+ }, [gameStarted, gameOver, isPaused, moveSnake]);
+
+ useEffect(() => {
+ const handleKeyPress = (e: KeyboardEvent) => {
+ if (e.code === "Space") {
+ e.preventDefault();
+ if (gameOver) {
+ resetGame();
+ } else if (!gameStarted) {
+ setGameStarted(true);
+ }
+ return;
+ }
+
+ if (e.code === "KeyP") {
+ e.preventDefault();
+ if (gameStarted && !gameOver) {
+ setIsPaused((prev) => !prev);
+ }
+ return;
+ }
+
+ if (!gameStarted || gameOver || isPaused) return;
+
+ let newDirection: Direction | null = null;
+
+ switch (e.key) {
+ case "ArrowUp":
+ if (directionRef.current !== "DOWN") {
+ newDirection = "UP";
+ }
+ break;
+ case "ArrowDown":
+ if (directionRef.current !== "UP") {
+ newDirection = "DOWN";
+ }
+ break;
+ case "ArrowLeft":
+ if (directionRef.current !== "RIGHT") {
+ newDirection = "LEFT";
+ }
+ break;
+ case "ArrowRight":
+ if (directionRef.current !== "LEFT") {
+ newDirection = "RIGHT";
+ }
+ break;
+ }
+
+ if (newDirection) {
+ e.preventDefault();
+ directionRef.current = newDirection;
+ setDirection(newDirection);
+ }
+ };
+
+ window.addEventListener("keydown", handleKeyPress);
+ return () => window.removeEventListener("keydown", handleKeyPress);
+ }, [gameOver, gameStarted, isPaused, resetGame]);
+
+ const handleTouchMove = (dir: Direction) => {
+ if (!gameStarted) {
+ setGameStarted(true);
+ directionRef.current = dir;
+ setDirection(dir);
+ return;
+ }
+ if (gameOver || isPaused) return;
+
+ let canMove = false;
+
+ switch (dir) {
+ case "UP":
+ canMove = directionRef.current !== "DOWN";
+ break;
+ case "DOWN":
+ canMove = directionRef.current !== "UP";
+ break;
+ case "LEFT":
+ canMove = directionRef.current !== "RIGHT";
+ break;
+ case "RIGHT":
+ canMove = directionRef.current !== "LEFT";
+ break;
+ }
+
+ if (canMove) {
+ directionRef.current = dir;
+ setDirection(dir);
+ }
+ };
+
+ const handleCanvasClick = () => {
+ if (gameOver) {
+ resetGame();
+ } else if (!gameStarted) {
+ setGameStarted(true);
+ }
+ };
+
+ return (
+
+
+
+
+ {t("score")}: {score}
+
+
+ {t("highScore")}: {highScore}
+
+
+
+
+
+
+ {!gameStarted && !gameOver && (
+
+
+ {t("pressToStart")}
+ {t("pauseGame")}
+
+
+ )}
+
+ {gameOver && (
+
+
+
+ {t("gameOver")}
+
+
+ {t("score")}: {score}
+
+ {t("pressToRestart")}
+
+
+ )}
+
+ {isPaused && (
+
+
+
+ {t("paused")}
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t("useArrowKeys")}
+ {t("tapToMove")}
+
+
+
+ );
+};
diff --git a/app/_components/FeatureComponents/Layout/Sidebar.tsx b/app/_components/FeatureComponents/Layout/Sidebar.tsx
index 0102260..994e51c 100644
--- a/app/_components/FeatureComponents/Layout/Sidebar.tsx
+++ b/app/_components/FeatureComponents/Layout/Sidebar.tsx
@@ -2,15 +2,15 @@ import { cn } from "@/app/_utils/global-utils";
import { HTMLAttributes, forwardRef, useState, useEffect } from "react";
import React from "react";
import {
- ChevronLeft,
- ChevronRight,
- Server,
- Menu,
- X,
- Cpu,
- HardDrive,
- Wifi,
-} from "lucide-react";
+ CaretLeftIcon,
+ CaretRightIcon,
+ HardDrivesIcon,
+ ListIcon,
+ XIcon,
+ CpuIcon,
+ HardDriveIcon,
+ WifiHighIcon,
+} from "@phosphor-icons/react";
import { useTranslations } from "next-intl";
export interface SidebarProps extends HTMLAttributes {
@@ -54,18 +54,18 @@ export const Sidebar = forwardRef(
<>
setIsMobileOpen(false)}
@@ -74,7 +74,7 @@ export const Sidebar = forwardRef(
(
>
-
-
-
-
-
- {(!isCollapsed || !isCollapsed) && (
-
- {t("sidebar.systemOverview")}
-
- )}
-
-
-
{isCollapsed ? (
@@ -131,22 +113,22 @@ export const Sidebar = forwardRef(
{quickStats ? (
<>
-
-
+
+
{quickStats.cpu}%
-
-
+
+
{quickStats.memory}%
-
-
+
+
{quickStats.network}
@@ -163,9 +145,9 @@ export const Sidebar = forwardRef(
return (
-
+
);
}
diff --git a/app/_components/FeatureComponents/Layout/TabbedInterface.tsx b/app/_components/FeatureComponents/Layout/TabbedInterface.tsx
index 06d006d..ae04087 100644
--- a/app/_components/FeatureComponents/Layout/TabbedInterface.tsx
+++ b/app/_components/FeatureComponents/Layout/TabbedInterface.tsx
@@ -5,7 +5,7 @@ import { CronJobList } from "@/app/_components/FeatureComponents/Cronjobs/CronJo
import { ScriptsManager } from "@/app/_components/FeatureComponents/Scripts/ScriptsManager";
import { CronJob } from "@/app/_utils/cronjob-utils";
import { Script } from "@/app/_utils/scripts-utils";
-import { Clock, FileText } from "lucide-react";
+import { ClockIcon, FileTextIcon } from "@phosphor-icons/react";
import { useTranslations } from "next-intl";
interface TabbedInterfaceProps {
@@ -24,33 +24,31 @@ export const TabbedInterface = ({
return (
-
-
+
+
diff --git a/app/_components/FeatureComponents/LoginForm/LoginForm.tsx b/app/_components/FeatureComponents/LoginForm/LoginForm.tsx
index fb5fb6a..3583aa1 100644
--- a/app/_components/FeatureComponents/LoginForm/LoginForm.tsx
+++ b/app/_components/FeatureComponents/LoginForm/LoginForm.tsx
@@ -12,7 +12,7 @@ import {
CardDescription,
CardContent,
} from "@/app/_components/GlobalComponents/Cards/Card";
-import { Lock, Eye, EyeOff, Shield, AlertTriangle, Loader2 } from "lucide-react";
+import { LockIcon, EyeIcon, EyeSlashIcon, ShieldIcon, WarningIcon, CircleNotchIcon } from "@phosphor-icons/react";
interface LoginFormProps {
hasPassword?: boolean;
@@ -88,7 +88,7 @@ export const LoginForm = ({
-
+
{t("login.redirectingToOIDC")}
@@ -105,15 +105,15 @@ export const LoginForm = ({
-
+
{t("login.welcomeTitle")}
{hasPassword && hasOIDC
? t("login.signInWithPasswordOrSSO")
: hasOIDC
- ? t("login.signInWithSSO")
- : t("login.enterPasswordToContinue")}
+ ? t("login.signInWithSSO")
+ : t("login.enterPasswordToContinue")}
@@ -121,7 +121,7 @@ export const LoginForm = ({
{!hasPassword && !hasOIDC && (
-
+
{t("login.authenticationNotConfigured")}
@@ -152,9 +152,9 @@ export const LoginForm = ({
disabled={isLoading}
>
{showPassword ? (
-
+
) : (
-
+
)}
@@ -175,7 +175,7 @@ export const LoginForm = ({
-
+
{t("login.orContinueWith")}
@@ -190,7 +190,7 @@ export const LoginForm = ({
onClick={handleOIDCLogin}
disabled={isLoading}
>
-
+
{isLoading ? t("login.redirecting") : t("login.signInWithSSO")}
)}
@@ -203,7 +203,7 @@ export const LoginForm = ({
{version && (
-
+
Cr*nMaster {t("common.version", { version })}
diff --git a/app/_components/FeatureComponents/LoginForm/LogoutButton.tsx b/app/_components/FeatureComponents/LoginForm/LogoutButton.tsx
index d3c05d4..3141841 100644
--- a/app/_components/FeatureComponents/LoginForm/LogoutButton.tsx
+++ b/app/_components/FeatureComponents/LoginForm/LogoutButton.tsx
@@ -3,7 +3,7 @@
import { useState } from "react";
import { useRouter } from "next/navigation";
import { Button } from "@/app/_components/GlobalComponents/UIElements/Button";
-import { LogOut } from "lucide-react";
+import { SignOutIcon } from "@phosphor-icons/react";
export const LogoutButton = () => {
const [isLoading, setIsLoading] = useState(false);
@@ -35,7 +35,7 @@ export const LogoutButton = () => {
disabled={isLoading}
title="Logout"
>
-
+
Logout
);
diff --git a/app/_components/FeatureComponents/Modals/CloneScriptModal.tsx b/app/_components/FeatureComponents/Modals/CloneScriptModal.tsx
index befa1df..47e3b13 100644
--- a/app/_components/FeatureComponents/Modals/CloneScriptModal.tsx
+++ b/app/_components/FeatureComponents/Modals/CloneScriptModal.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
-import { Copy } from "lucide-react";
+import { CopyIcon } from "@phosphor-icons/react";
import { Button } from "@/app/_components/GlobalComponents/UIElements/Button";
import { Modal } from "@/app/_components/GlobalComponents/UIElements/Modal";
import { Input } from "@/app/_components/GlobalComponents/FormElements/Input";
@@ -89,7 +89,7 @@ export const CloneScriptModal = ({
>
) : (
<>
-
+
Clone Script
>
)}
diff --git a/app/_components/FeatureComponents/Modals/CloneTaskModal.tsx b/app/_components/FeatureComponents/Modals/CloneTaskModal.tsx
index 5244c8d..9806027 100644
--- a/app/_components/FeatureComponents/Modals/CloneTaskModal.tsx
+++ b/app/_components/FeatureComponents/Modals/CloneTaskModal.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
-import { Copy } from "lucide-react";
+import { CopyIcon } from "@phosphor-icons/react";
import { Button } from "@/app/_components/GlobalComponents/UIElements/Button";
import { Modal } from "@/app/_components/GlobalComponents/UIElements/Modal";
import { Input } from "@/app/_components/GlobalComponents/FormElements/Input";
@@ -89,7 +89,7 @@ export const CloneTaskModal = ({
>
) : (
<>
-
+
Clone Cron Job
>
)}
diff --git a/app/_components/FeatureComponents/Modals/CreateScriptModal.tsx b/app/_components/FeatureComponents/Modals/CreateScriptModal.tsx
index 74905c7..5cf8f1c 100644
--- a/app/_components/FeatureComponents/Modals/CreateScriptModal.tsx
+++ b/app/_components/FeatureComponents/Modals/CreateScriptModal.tsx
@@ -1,6 +1,6 @@
"use client";
-import { Plus } from "lucide-react";
+import { PlusIcon } from "@phosphor-icons/react";
import { ScriptModal } from "@/app/_components/FeatureComponents/Modals/ScriptModal";
interface CreateScriptModalProps {
@@ -35,7 +35,7 @@ export const CreateScriptModal = ({
onSubmit={onSubmit}
title="Create New Script"
submitButtonText="Create Script"
- submitButtonIcon={ }
+ submitButtonIcon={ }
form={form}
onFormChange={onFormChange}
isDraft={isDraft}
diff --git a/app/_components/FeatureComponents/Modals/CreateTaskModal.tsx b/app/_components/FeatureComponents/Modals/CreateTaskModal.tsx
index a850a2f..c026225 100644
--- a/app/_components/FeatureComponents/Modals/CreateTaskModal.tsx
+++ b/app/_components/FeatureComponents/Modals/CreateTaskModal.tsx
@@ -4,10 +4,11 @@ import { useState, useEffect } from "react";
import { Modal } from "@/app/_components/GlobalComponents/UIElements/Modal";
import { Button } from "@/app/_components/GlobalComponents/UIElements/Button";
import { Input } from "@/app/_components/GlobalComponents/FormElements/Input";
+import { Switch } from "@/app/_components/GlobalComponents/UIElements/Switch";
import { CronExpressionHelper } from "@/app/_components/FeatureComponents/Scripts/CronExpressionHelper";
import { SelectScriptModal } from "@/app/_components/FeatureComponents/Modals/SelectScriptModal";
import { UserSwitcher } from "@/app/_components/FeatureComponents/User/UserSwitcher";
-import { Plus, Terminal, FileText, X, FileOutput } from "lucide-react";
+import { PlusIcon, TerminalIcon, FileTextIcon, XIcon, FileArrowDownIcon } from "@phosphor-icons/react";
import { getScriptContent } from "@/app/_server/actions/scripts";
import { getHostScriptPath } from "@/app/_server/actions/scripts";
import { useTranslations } from "next-intl";
@@ -100,7 +101,7 @@ export const CreateTaskModal = ({
onFormChange({ user })}
+ onUserChange={(user: string) => onFormChange({ user })}
/>
@@ -124,13 +125,13 @@ export const CreateTaskModal = ({