mirror of
https://github.com/fosrl/pangolin.git
synced 2025-07-01 09:34:54 +02:00
use bottom sheet instead of vaul drawer
This commit is contained in:
parent
91d314c4cc
commit
e6263567a9
13 changed files with 284 additions and 121 deletions
|
@ -4,7 +4,7 @@ WORKDIR /app
|
|||
|
||||
COPY package.json ./
|
||||
|
||||
RUN npm install --legacy-peer-deps
|
||||
RUN npm install
|
||||
|
||||
COPY . .
|
||||
|
||||
|
@ -20,7 +20,7 @@ WORKDIR /app
|
|||
|
||||
COPY package.json ./
|
||||
|
||||
RUN npm install --omit=dev --legacy-peer-deps
|
||||
RUN npm install --omit=dev
|
||||
|
||||
COPY --from=builder /app/.next ./.next
|
||||
COPY --from=builder /app/dist ./dist
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import express, { Request, Response } from "express";
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import cookieParser from "cookie-parser";
|
||||
import config from "@server/config";
|
||||
|
|
|
@ -14,7 +14,10 @@ const portSchema = z.number().positive().gt(0).lte(65535);
|
|||
|
||||
const environmentSchema = z.object({
|
||||
app: z.object({
|
||||
base_url: z.string().url().transform((url) => url.toLowerCase()),
|
||||
base_url: z
|
||||
.string()
|
||||
.url()
|
||||
.transform((url) => url.toLowerCase()),
|
||||
log_level: z.enum(["debug", "info", "warn", "error"]),
|
||||
save_logs: z.boolean()
|
||||
}),
|
||||
|
@ -76,6 +79,7 @@ const environmentSchema = z.object({
|
|||
.optional()
|
||||
});
|
||||
|
||||
export function getConfig() {
|
||||
const loadConfig = (configPath: string) => {
|
||||
try {
|
||||
const yamlContent = fs.readFileSync(configPath, "utf8");
|
||||
|
@ -156,7 +160,8 @@ process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags
|
|||
?.require_email_verification
|
||||
? "true"
|
||||
: "false";
|
||||
process.env.SESSION_COOKIE_NAME = parsedConfig.data.server.session_cookie_name;
|
||||
process.env.SESSION_COOKIE_NAME =
|
||||
parsedConfig.data.server.session_cookie_name;
|
||||
process.env.RESOURCE_SESSION_COOKIE_NAME =
|
||||
parsedConfig.data.server.resource_session_cookie_name;
|
||||
process.env.EMAIL_ENABLED = parsedConfig.data.email ? "true" : "false";
|
||||
|
@ -169,4 +174,7 @@ process.env.DISABLE_USER_CREATE_ORG = parsedConfig.data.flags
|
|||
? "true"
|
||||
: "false";
|
||||
|
||||
export default parsedConfig.data;
|
||||
return parsedConfig.data;
|
||||
}
|
||||
|
||||
export default getConfig();
|
||||
|
|
|
@ -68,7 +68,7 @@ export const SendInviteLink = ({
|
|||
<Section className="text-center my-6">
|
||||
<Button
|
||||
href={inviteLink}
|
||||
className="rounded-lg bg-primary px-[12px] py-[9px] text-center font-semibold text-white cursor-pointer"
|
||||
className="rounded-lg bg-primary px-[12px] py-[9px] text-center font-semibold text-white cursor-pointer text-xl"
|
||||
>
|
||||
Accept invitation to {orgName}
|
||||
</Button>
|
||||
|
|
|
@ -421,6 +421,7 @@ export default function ResourceAuthenticationPage() {
|
|||
<FormItem className="flex flex-col items-start">
|
||||
<FormLabel>Roles</FormLabel>
|
||||
<FormControl>
|
||||
{/* @ts-ignore */}
|
||||
<TagInput
|
||||
{...field}
|
||||
activeTagIndex={
|
||||
|
@ -454,9 +455,9 @@ export default function ResourceAuthenticationPage() {
|
|||
tag: {
|
||||
body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
|
||||
},
|
||||
input: "border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
|
||||
input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
|
||||
inlineTagsContainer:
|
||||
"bg-transparent"
|
||||
"bg-transparent p-2"
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
@ -476,6 +477,7 @@ export default function ResourceAuthenticationPage() {
|
|||
<FormItem className="flex flex-col items-start">
|
||||
<FormLabel>Users</FormLabel>
|
||||
<FormControl>
|
||||
{/* @ts-ignore */}
|
||||
<TagInput
|
||||
{...field}
|
||||
activeTagIndex={
|
||||
|
@ -509,9 +511,9 @@ export default function ResourceAuthenticationPage() {
|
|||
tag: {
|
||||
body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
|
||||
},
|
||||
input: "border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
|
||||
input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
|
||||
inlineTagsContainer:
|
||||
"bg-transparent"
|
||||
"bg-transparent p-2"
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
@ -649,6 +651,7 @@ export default function ResourceAuthenticationPage() {
|
|||
Whitelisted Emails
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
{/* @ts-ignore */}
|
||||
<TagInput
|
||||
{...field}
|
||||
activeTagIndex={
|
||||
|
@ -691,9 +694,9 @@ export default function ResourceAuthenticationPage() {
|
|||
tag: {
|
||||
body: "bg-muted hover:bg-accent text-foreground py-2 px-3 rounded-full"
|
||||
},
|
||||
input: "border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
|
||||
input: "text-base md:text-sm border-none bg-transparent text-inherit placeholder:text-inherit shadow-none",
|
||||
inlineTagsContainer:
|
||||
"bg-transparent"
|
||||
"bg-transparent p-2"
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
|
|
@ -50,7 +50,7 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
|||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<Link href="../../">Sites</Link>
|
||||
<Link href="../">Sites</Link>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 20 5.0% 10.0%;
|
||||
--foreground: 20 0.0% 10.0%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 20 5.0% 10.0%;
|
||||
--card-foreground: 20 0.0% 10.0%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 20 5.0% 10.0%;
|
||||
--popover-foreground: 20 0.0% 10.0%;
|
||||
--primary: 24.6 95% 53.1%;
|
||||
--primary-foreground: 60 9.1% 97.8%;
|
||||
--secondary: 60 4.8% 95.9%;
|
||||
|
@ -33,11 +33,11 @@
|
|||
}
|
||||
|
||||
.dark {
|
||||
--background: 20 5.0% 10.0%;
|
||||
--background: 20 0.0% 10.0%;
|
||||
--foreground: 60 9.1% 97.8%;
|
||||
--card: 20 5.0% 10.0%;
|
||||
--card: 20 0.0% 10.0%;
|
||||
--card-foreground: 60 9.1% 97.8%;
|
||||
--popover: 20 5.0% 10.0%;
|
||||
--popover: 20 0.0% 10.0%;
|
||||
--popover-foreground: 60 9.1% 97.8%;
|
||||
--primary: 20.5 90.2% 48.2%;
|
||||
--primary-foreground: 60 9.1% 97.8%;
|
||||
|
|
|
@ -24,6 +24,15 @@ import {
|
|||
DrawerTitle,
|
||||
DrawerTrigger
|
||||
} from "@/components/ui/drawer";
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetFooter,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger
|
||||
} from "./ui/sheet";
|
||||
|
||||
interface BaseProps {
|
||||
children: React.ReactNode;
|
||||
|
@ -44,7 +53,7 @@ const desktop = "(min-width: 768px)";
|
|||
const Credenza = ({ children, ...props }: RootCredenzaProps) => {
|
||||
const isDesktop = useMediaQuery(desktop);
|
||||
// const isDesktop = true;
|
||||
const Credenza = isDesktop ? Dialog : Drawer;
|
||||
const Credenza = isDesktop ? Dialog : Sheet;
|
||||
|
||||
return <Credenza {...props}>{children}</Credenza>;
|
||||
};
|
||||
|
@ -53,7 +62,7 @@ const CredenzaTrigger = ({ className, children, ...props }: CredenzaProps) => {
|
|||
const isDesktop = useMediaQuery(desktop);
|
||||
// const isDesktop = true;
|
||||
|
||||
const CredenzaTrigger = isDesktop ? DialogTrigger : DrawerTrigger;
|
||||
const CredenzaTrigger = isDesktop ? DialogTrigger : SheetTrigger;
|
||||
|
||||
return (
|
||||
<CredenzaTrigger className={className} {...props}>
|
||||
|
@ -79,10 +88,14 @@ const CredenzaContent = ({ className, children, ...props }: CredenzaProps) => {
|
|||
const isDesktop = useMediaQuery(desktop);
|
||||
// const isDesktop = true;
|
||||
|
||||
const CredenzaContent = isDesktop ? DialogContent : DrawerContent;
|
||||
const CredenzaContent = isDesktop ? DialogContent : SheetContent;
|
||||
|
||||
return (
|
||||
<CredenzaContent className={className} {...props}>
|
||||
<CredenzaContent
|
||||
className={cn("overflow-y-auto max-h-screen", className)}
|
||||
{...props}
|
||||
side={"bottom"}
|
||||
>
|
||||
{children}
|
||||
</CredenzaContent>
|
||||
);
|
||||
|
@ -98,7 +111,7 @@ const CredenzaDescription = ({
|
|||
|
||||
const CredenzaDescription = isDesktop
|
||||
? DialogDescription
|
||||
: DrawerDescription;
|
||||
: SheetDescription;
|
||||
|
||||
return (
|
||||
<CredenzaDescription className={className} {...props}>
|
||||
|
@ -111,7 +124,7 @@ const CredenzaHeader = ({ className, children, ...props }: CredenzaProps) => {
|
|||
const isDesktop = useMediaQuery(desktop);
|
||||
// const isDesktop = true;
|
||||
|
||||
const CredenzaHeader = isDesktop ? DialogHeader : DrawerHeader;
|
||||
const CredenzaHeader = isDesktop ? DialogHeader : SheetHeader;
|
||||
|
||||
return (
|
||||
<CredenzaHeader className={className} {...props}>
|
||||
|
@ -124,7 +137,7 @@ const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => {
|
|||
const isDesktop = useMediaQuery(desktop);
|
||||
// const isDesktop = true;
|
||||
|
||||
const CredenzaTitle = isDesktop ? DialogTitle : DrawerTitle;
|
||||
const CredenzaTitle = isDesktop ? DialogTitle : SheetTitle;
|
||||
|
||||
return (
|
||||
<CredenzaTitle className={className} {...props}>
|
||||
|
@ -134,25 +147,24 @@ const CredenzaTitle = ({ className, children, ...props }: CredenzaProps) => {
|
|||
};
|
||||
|
||||
const CredenzaBody = ({ className, children, ...props }: CredenzaProps) => {
|
||||
return (
|
||||
<div className={cn("px-4 md:px-0 mb-4", className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
// return (
|
||||
// <div className={cn("px-0 mb-4", className)} {...props}>
|
||||
// <div className={cn("px-4 md:px-0 mb-4", className)} {...props}>
|
||||
// {children}
|
||||
// </div>
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className={cn("px-0 mb-4", className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CredenzaFooter = ({ className, children, ...props }: CredenzaProps) => {
|
||||
const isDesktop = useMediaQuery(desktop);
|
||||
// const isDesktop = true;
|
||||
|
||||
const CredenzaFooter = isDesktop ? DialogFooter : DrawerFooter;
|
||||
const CredenzaFooter = isDesktop ? DialogFooter : SheetFooter;
|
||||
|
||||
return (
|
||||
<CredenzaFooter className={className} {...props}>
|
||||
|
|
|
@ -224,7 +224,7 @@ export default function Enable2FaForm({ open, setOpen }: Enable2FaProps) {
|
|||
<div className="h-[250px] mx-auto flex items-center justify-center">
|
||||
<QRCodeCanvas value={secretUri} size={200} />
|
||||
</div>
|
||||
<CopyTextBox text={secretKey} wrapText={false} />
|
||||
<CopyTextBox text={secretUri} wrapText={false} />
|
||||
|
||||
<Form {...confirmForm}>
|
||||
<form
|
||||
|
|
140
src/components/ui/sheet.tsx
Normal file
140
src/components/ui/sheet.tsx
Normal file
|
@ -0,0 +1,140 @@
|
|||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Sheet = SheetPrimitive.Root
|
||||
|
||||
const SheetTrigger = SheetPrimitive.Trigger
|
||||
|
||||
const SheetClose = SheetPrimitive.Close
|
||||
|
||||
const SheetPortal = SheetPrimitive.Portal
|
||||
|
||||
const SheetOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
))
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
interface SheetContentProps
|
||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(sheetVariants({ side }), className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
))
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName
|
||||
|
||||
const SheetHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
SheetHeader.displayName = "SheetHeader"
|
||||
|
||||
const SheetFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
SheetFooter.displayName = "SheetFooter"
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName
|
||||
|
||||
const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetPortal,
|
||||
SheetOverlay,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
}
|
|
@ -25,7 +25,7 @@ const ToastViewport = React.forwardRef<
|
|||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
||||
|
||||
const toastVariants = cva(
|
||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
||||
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-3 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
|
@ -6,7 +6,7 @@ import * as React from "react";
|
|||
import type { ToastActionElement, ToastProps } from "@/components/ui/toast";
|
||||
|
||||
const TOAST_LIMIT = 3;
|
||||
const TOAST_REMOVE_DELAY = 5 * 1000;
|
||||
const TOAST_REMOVE_DELAY = 1 * 1000;
|
||||
|
||||
type ToasterToast = ToastProps & {
|
||||
id: string;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { type ThemeProviderProps } from "next-themes/dist/types";
|
||||
|
||||
type ThemeProviderProps = React.ComponentProps<typeof NextThemesProvider>;
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue