diff --git a/client/src/components/layouts/sidebar.tsx b/client/src/components/layouts/sidebar.tsx index 8c0bb49..4a07659 100644 --- a/client/src/components/layouts/sidebar.tsx +++ b/client/src/components/layouts/sidebar.tsx @@ -28,6 +28,11 @@ export function Sidebar() { href: "/", icon: , }, + { + title: "Organizations", + href: "/organizations", + icon: , + }, { title: "Domains", href: "/domains", diff --git a/client/src/components/shared/back-button.tsx b/client/src/components/shared/back-button.tsx new file mode 100644 index 0000000..3557f97 --- /dev/null +++ b/client/src/components/shared/back-button.tsx @@ -0,0 +1,35 @@ +import { Button } from "@/components/ui/button"; +import { ChevronLeft } from "lucide-react"; +import { useLocation } from "wouter"; + +interface BackButtonProps { + to?: string; + fallbackPath?: string; + className?: string; +} + +export function BackButton({ to, fallbackPath = "/", className = "" }: BackButtonProps) { + const [_, setLocation] = useLocation(); + + const handleBack = () => { + if (to) { + setLocation(to); + } else if (window.history.length > 2) { + window.history.back(); + } else { + setLocation(fallbackPath); + } + }; + + return ( + + + Back + + ); +} \ No newline at end of file diff --git a/client/src/pages/dns-records.tsx b/client/src/pages/dns-records.tsx index b041e14..9cd903c 100644 --- a/client/src/pages/dns-records.tsx +++ b/client/src/pages/dns-records.tsx @@ -54,10 +54,16 @@ import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger +} from "@/components/ui/tooltip"; import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator } from "@/components/ui/breadcrumb"; import { RecentActivity } from "@/components/activity/recent-activity"; import { Pagination } from "@/components/shared/pagination"; -import { Loader2, Home, Plus, Pencil, Trash2, ArrowLeft } from "lucide-react"; +import { Loader2, Home, Plus, Pencil, Trash2, ArrowLeft, FileText } from "lucide-react"; import { formatDistanceToNow } from "date-fns"; import { Link } from "wouter"; @@ -69,6 +75,8 @@ const dnsRecordSchema = z.object({ ttl: z.number().int().min(1).default(3600), proxied: z.boolean().default(false), isActive: z.boolean().default(true), + isAutoIP: z.boolean().default(false), + notes: z.string().optional(), }); export default function DnsRecordsPage() { @@ -113,6 +121,8 @@ export default function DnsRecordsPage() { ttl: 3600, proxied: false, isActive: true, + isAutoIP: false, + notes: "", }, }); @@ -123,9 +133,11 @@ export default function DnsRecordsPage() { name: selectedRecord.name, type: selectedRecord.type as any, content: selectedRecord.content, - ttl: selectedRecord.ttl, - proxied: selectedRecord.proxied, + ttl: selectedRecord.ttl ?? 3600, + proxied: selectedRecord.proxied ?? false, isActive: selectedRecord.isActive, + isAutoIP: selectedRecord.isAutoIP ?? false, + notes: selectedRecord.notes ?? "", }); } else { form.reset({ @@ -135,6 +147,8 @@ export default function DnsRecordsPage() { ttl: 3600, proxied: false, isActive: true, + isAutoIP: false, + notes: "", }); } }, [selectedRecord, form]); @@ -289,7 +303,7 @@ export default function DnsRecordsPage() { - {domain?.name || 'Loading...'} + {domain?.name || 'Loading...'} @@ -345,6 +359,11 @@ export default function DnsRecordsPage() { {record.name} {record.type} + {record.isAutoIP && (record.type === 'A' || record.type === 'AAAA') && ( + + Auto IP + + )} {record.content.length > 30 @@ -369,21 +388,37 @@ export default function DnsRecordsPage() { : "Never"} - handleEditRecord(record)} - > - - - handleDeleteRecord(record)} - className="text-destructive" - > - - + + {record.notes && ( + + + + + + + + + {record.notes} + + + + )} + handleEditRecord(record)} + > + + + handleDeleteRecord(record)} + className="text-destructive" + > + + + ))} @@ -559,6 +594,46 @@ export default function DnsRecordsPage() { )} /> + {(form.watch("type") === "A" || form.watch("type") === "AAAA") && ( + ( + + + Auto IP Address + + Automatically determine IP address using STUN + + + + + + + )} + /> + )} + + ( + + Notes + + + + + Add any additional information about this record + + + + )} + /> + + {(form.watch("type") === "A" || form.watch("type") === "AAAA") && ( + ( + + + Auto IP Address + + Automatically determine IP address using STUN + + + + + + + )} + /> + )} + + ( + + Notes + + + + + Add any additional information about this record + + + + )} + /> + { const id = this.providerIdCounter++; const createdAt = new Date(); - const newProvider: Provider = { id, ...provider, createdAt }; + const newProvider: Provider = { + id, + name: provider.name, + type: provider.type, + credentials: provider.credentials ?? null, + isActive: provider.isActive ?? true, + createdAt + }; this.providersMap.set(id, newProvider); return newProvider; } @@ -341,7 +361,17 @@ export class MemStorage implements IStorage { async createApiToken(token: InsertApiToken): Promise { const id = this.apiTokenIdCounter++; const createdAt = new Date(); - const newToken: ApiToken = { id, ...token, createdAt }; + const newToken: ApiToken = { + id, + name: token.name, + token: token.token, + organizationId: token.organizationId, + permissions: token.permissions ?? null, + createdBy: token.createdBy, + isActive: token.isActive ?? true, + expiresAt: token.expiresAt ?? null, + createdAt + }; this.apiTokensMap.set(id, newToken); return newToken; } @@ -374,9 +404,9 @@ export class MemStorage implements IStorage { id, recordId, action, - previousValue, - newValue, - userId, + previousValue: previousValue ?? null, + newValue: newValue ?? null, + userId: userId ?? null, timestamp }; diff --git a/shared/schema.ts b/shared/schema.ts index faca0c5..19da11a 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -60,6 +60,8 @@ export const dnsRecords = pgTable("dns_records", { ttl: integer("ttl").default(3600), proxied: boolean("proxied").default(false), isActive: boolean("is_active").default(true).notNull(), + isAutoIP: boolean("is_auto_ip").default(false).notNull(), + notes: text("notes"), lastUpdated: timestamp("last_updated"), createdAt: timestamp("created_at").defaultNow().notNull(), }); @@ -117,6 +119,8 @@ export const insertDnsRecordSchema = createInsertSchema(dnsRecords).pick({ ttl: true, proxied: true, isActive: true, + isAutoIP: true, + notes: true, }); export const insertProviderSchema = createInsertSchema(providers).pick({
{record.notes}