mirror of
https://github.com/fergalmoran/kidarr-server.git
synced 2026-02-19 06:15:09 +00:00
Improve dashboard layout
This commit is contained in:
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { api } from '@/trpc/server';
|
||||
import ChildrenFilter from '@/components/children/children-filter';
|
||||
import dynamic from 'next/dynamic';
|
||||
import ChildModel from '@/lib/models/child';
|
||||
import { MapViewTypeSelector } from '@/components/maps/map-viewtype-selector';
|
||||
|
||||
const Dashboard = async () => {
|
||||
const kids = await api.child.mine.query();
|
||||
@@ -10,10 +10,11 @@ const Dashboard = async () => {
|
||||
ssr: false,
|
||||
});
|
||||
return <div>
|
||||
<div className="z-10">
|
||||
<div className="flex flex-row justify-between">
|
||||
<ChildrenFilter kids={kids} />
|
||||
<MapViewTypeSelector />
|
||||
</div>
|
||||
<div className="z-0 mt-4">
|
||||
<div className="mt-4">
|
||||
<Map kids={kids} />
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import "@/styles/globals.css";
|
||||
import '@/styles/globals.css';
|
||||
|
||||
import { Inter } from "next/font/google";
|
||||
import { cookies } from "next/headers";
|
||||
import { ABeeZee as TheFont } from 'next/font/google';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
import { TRPCReactProvider } from "@/trpc/react";
|
||||
import { ThemeProvider } from "@/components/providers/theme-provider";
|
||||
import { type Metadata } from "next";
|
||||
import NextAuthProvider from "@/lib/services/auth/provider";
|
||||
import { TRPCReactProvider } from '@/trpc/react';
|
||||
import { ThemeProvider } from '@/components/providers/theme-provider';
|
||||
import { type Metadata } from 'next';
|
||||
import NextAuthProvider from '@/lib/services/auth/provider';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
variable: "--font-sans",
|
||||
const inter = TheFont({
|
||||
weight: '400',
|
||||
subsets: ['latin'],
|
||||
variable: '--font-sans',
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "ParentGrine Falcon",
|
||||
description: "Laser focused on your kids",
|
||||
manifest: "/site.webmanifest",
|
||||
title: 'ParentGrine Falcon',
|
||||
description: 'Laser focused on your kids',
|
||||
manifest: '/site.webmanifest',
|
||||
icons: {
|
||||
icon: "/favicon.ico",
|
||||
icon: '/favicon.ico',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,15 +30,15 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning={true}>
|
||||
<body className={`font-sans ${inter.variable}`}>
|
||||
<NextAuthProvider>
|
||||
<TRPCReactProvider cookies={cookies().toString()}>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</TRPCReactProvider>
|
||||
</NextAuthProvider>
|
||||
</body>
|
||||
<body className={`font-sans ${inter.variable}`}>
|
||||
<NextAuthProvider>
|
||||
<TRPCReactProvider cookies={cookies().toString()}>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</TRPCReactProvider>
|
||||
</NextAuthProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Copy,
|
||||
Cable,
|
||||
Edit,
|
||||
Edit2, Radar, PhoneCall, Smartphone,
|
||||
Edit2, Radar, PhoneCall, Smartphone, Waypoints, LocateFixed,
|
||||
} from 'lucide-react';
|
||||
|
||||
export type Icon = LucideIcon;
|
||||
@@ -34,12 +34,14 @@ export const Icons = {
|
||||
device: Smartphone,
|
||||
edit: Edit,
|
||||
sun: SunMedium,
|
||||
location: LocateFixed,
|
||||
login: LogIn,
|
||||
message: MessageCircleMore,
|
||||
mobile: TabletSmartphone,
|
||||
moon: Moon,
|
||||
presence: Radar,
|
||||
rocket: Rocket,
|
||||
route: Waypoints,
|
||||
save: Save,
|
||||
spinner: Loader2,
|
||||
twitter: Twitter,
|
||||
|
||||
@@ -41,46 +41,46 @@ const MapMarker: React.FC<MapMarkerProps> = (
|
||||
setPosition([ping.location.latitude, ping.location.longitude]);
|
||||
},
|
||||
});
|
||||
return <Marker
|
||||
position={position}
|
||||
icon={_getIcon(avatar)}
|
||||
>
|
||||
<Popup>
|
||||
<Card className="w-full max-w-md grid gap-4">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar className="h-12 w-12">
|
||||
<AvatarImage src={_getAvatarImage(avatar)} alt={childName} />
|
||||
<AvatarFallback>{getInitials(childName)}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="grid gap-1">
|
||||
<div className="text-lg font-bold">{childName}</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
<div className="flex flex-row items-center px-2 space-x-3">
|
||||
<Icons.device /> {deviceName}
|
||||
return <div className="animate-spin">
|
||||
<Marker
|
||||
position={position}
|
||||
icon={_getIcon(avatar)}
|
||||
>
|
||||
<Popup>
|
||||
<Card className="w-full max-w-md grid gap-4">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar className="h-12 w-12">
|
||||
<AvatarImage src={_getAvatarImage(avatar)} alt={childName} />
|
||||
<AvatarFallback>{getInitials(childName)}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="grid gap-1">
|
||||
<div className="text-lg font-bold">{childName}</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
<div className="flex flex-row items-center px-2 space-x-3">
|
||||
<Icons.device /> {deviceName}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Last seen - {timestamp.toLocaleDateString() + ' ' + timestamp.toLocaleTimeString()}
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="flex justify-end gap-4">
|
||||
<Link className="text-primary" href="#">
|
||||
<Icons.message className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link className="text-primary" href="#">
|
||||
<Icons.call className="h-6 w-6" />
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
</Popup>;
|
||||
</Marker>;
|
||||
;
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Last seen - {timestamp.toLocaleDateString() + ' ' + timestamp.toLocaleTimeString()}
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="flex justify-end gap-4">
|
||||
<Link className="text-primary" href="#">
|
||||
<Icons.message className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link className="text-primary" href="#">
|
||||
<Icons.call className="h-6 w-6" />
|
||||
</Link>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Popup>
|
||||
</Marker>
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default MapMarker;
|
||||
|
||||
21
src/components/maps/map-viewtype-selector.tsx
Normal file
21
src/components/maps/map-viewtype-selector.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
'use client';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import { Icons } from '@/components/icons';
|
||||
import React from 'react';
|
||||
|
||||
|
||||
export const MapViewTypeSelector = () => {
|
||||
const [currentView, setCurrentView] = React.useState('location');
|
||||
return (
|
||||
<ToggleGroup type="single" value={currentView}
|
||||
onValueChange={(value) => setCurrentView(value)}>
|
||||
<ToggleGroupItem value="location">
|
||||
<Icons.location className="w-4 h-4 mr-1" />
|
||||
Location
|
||||
</ToggleGroupItem>
|
||||
<ToggleGroupItem value="route">
|
||||
<Icons.route className="w-4 h-4 mr-1" />
|
||||
Route
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>);
|
||||
};
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client"
|
||||
'use client';
|
||||
|
||||
import * as React from "react"
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
||||
import { Circle } from "lucide-react"
|
||||
import * as React from 'react';
|
||||
import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
|
||||
import { Circle } from 'lucide-react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const RadioGroup = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||
@@ -12,13 +12,13 @@ const RadioGroup = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<RadioGroupPrimitive.Root
|
||||
className={cn("grid gap-2", className)}
|
||||
className={cn('grid gap-2', className)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
)
|
||||
})
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
|
||||
);
|
||||
});
|
||||
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
|
||||
|
||||
const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
@@ -28,8 +28,8 @@ const RadioGroupItem = React.forwardRef<
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
'aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
@@ -37,8 +37,8 @@ const RadioGroupItem = React.forwardRef<
|
||||
<Circle className="h-2.5 w-2.5 fill-current text-current" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
)
|
||||
})
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
|
||||
);
|
||||
});
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
||||
|
||||
export { RadioGroup, RadioGroupItem }
|
||||
export { RadioGroup, RadioGroupItem };
|
||||
|
||||
Reference in New Issue
Block a user