mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-01-21 06:34:30 +01:00
Merge Tag 'v0.2.28'
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
import ClientWrapper from './ClientWrapper'
|
||||
import Header from './Header'
|
||||
import Navigation from './Navigation'
|
||||
import PermissionError from './PermissionError'
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex flex-col h-screen bg-gray-100 dark:bg-gray-900 overflow-hidden">
|
||||
<Header className="sticky top-0 z-50" />
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<Navigation position='main' />
|
||||
<div className="flex-1 flex flex-col">
|
||||
<main className="flex-1 overflow-x-hidden overflow-y-auto bg-gray-100 dark:bg-gray-900 relative">
|
||||
{/* responsive container (optimized for mobile) */}
|
||||
<div className="mx-auto px-2 xs:px-4 py-8 max-w-sm xs:max-w-full">
|
||||
|
||||
<Header className="sticky top-0 z-50" />
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<Navigation position='main' />
|
||||
<div className="flex-1 flex flex-col">
|
||||
<main className="flex-1 overflow-x-hidden overflow-y-auto bg-gray-100 dark:bg-gray-900 relative">
|
||||
{/* responsive container (optimized for mobile) */}
|
||||
<div className="mx-auto px-2 xs:px-4 py-8 max-w-sm xs:max-w-full">
|
||||
<ClientWrapper>
|
||||
<PermissionError />
|
||||
{children}
|
||||
</ClientWrapper>
|
||||
</div>
|
||||
|
||||
43
components/PermissionError.tsx
Normal file
43
components/PermissionError.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
import { checkStartupPermissions } from '@/lib/startup-checks'
|
||||
import RecheckButton from './RecheckButton'
|
||||
|
||||
export default async function PermissionError() {
|
||||
const permissionResult = await checkStartupPermissions()
|
||||
|
||||
// If everything is fine, render nothing
|
||||
if (permissionResult.success) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Get error message
|
||||
const getErrorMessage = () => {
|
||||
return permissionResult.error?.message || 'Unknown permission error occurred.'
|
||||
}
|
||||
|
||||
return (
|
||||
<Alert variant="destructive" className="mb-4">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertTitle className="font-bold">Permission Error</AlertTitle>
|
||||
<AlertDescription className="mt-2 flex flex-col">
|
||||
<div className="space-y-3">
|
||||
<span className="text-sm">
|
||||
{getErrorMessage()}{" "}
|
||||
<a
|
||||
href="https://docs.habittrove.com/troubleshooting"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline hover:text-red-300"
|
||||
>
|
||||
Troubleshooting Guide
|
||||
</a>
|
||||
</span>
|
||||
<div>
|
||||
<RecheckButton />
|
||||
</div>
|
||||
</div>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
22
components/RecheckButton.tsx
Normal file
22
components/RecheckButton.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { RefreshCw } from 'lucide-react'
|
||||
|
||||
export default function RecheckButton() {
|
||||
const handleRecheck = () => {
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleRecheck}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="bg-red-50 border-red-300 text-red-700 hover:bg-red-100"
|
||||
>
|
||||
<RefreshCw className="h-4 w-4 mr-2" />
|
||||
Recheck
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
61
components/ui/alert.tsx
Normal file
61
components/ui/alert.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
warning:
|
||||
"border-yellow-200 bg-yellow-50 text-yellow-800 dark:border-yellow-800 dark:bg-yellow-950 dark:text-yellow-200 [&>svg]:text-yellow-600 dark:[&>svg]:text-yellow-400",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
Reference in New Issue
Block a user