mirror of
https://github.com/fergalmoran/kidarr-server.git
synced 2025-12-22 09:17:51 +00:00
Add default avatar and only show latest ping
This commit is contained in:
BIN
public/img/default-avatar.png
Normal file
BIN
public/img/default-avatar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -5,6 +5,7 @@ import { MapContainer, Marker, Popup, TileLayer, Circle } from 'react-leaflet';
|
||||
import { usePingSocket } from '@/lib/hooks/use-ping-socket';
|
||||
import ChildModel from '@/lib/models/child';
|
||||
import MapMarker from '@/components/maps/map-marker';
|
||||
import { getLatestPing } from '@/lib/utils';
|
||||
|
||||
type MainMapProps = {
|
||||
kids: ChildModel[];
|
||||
@@ -33,19 +34,21 @@ const MainMap: React.FC<MainMapProps> = ({ kids }) => {
|
||||
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
||||
<>
|
||||
{kids?.map((kid) =>
|
||||
kid.devices?.map((device) =>
|
||||
device.pings.map((ping) => (
|
||||
kid.devices?.map((device) => {
|
||||
const latestPing = getLatestPing(device.pings);
|
||||
return (
|
||||
<MapMarker
|
||||
key={ping.id}
|
||||
key={latestPing.id}
|
||||
childName={kid.name}
|
||||
avatar={kid.avatar}
|
||||
deviceName={device.deviceName}
|
||||
latitude={ping.latitude}
|
||||
longitude={ping.longitude}
|
||||
timestamp={ping.timestamp} />
|
||||
),
|
||||
),
|
||||
))}
|
||||
latitude={latestPing.latitude}
|
||||
longitude={latestPing.longitude}
|
||||
timestamp={latestPing.timestamp} />
|
||||
);
|
||||
},
|
||||
),
|
||||
)}
|
||||
</>
|
||||
</MapContainer>
|
||||
</div>;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import React from 'react';
|
||||
import L from 'leaflet';
|
||||
import { Circle, Marker, Popup } from 'react-leaflet';
|
||||
import { Marker, Popup } from 'react-leaflet';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '@/components/ui/card';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { getInitials } from '@/lib/utils';
|
||||
@@ -17,18 +15,17 @@ import { Icons } from '@/components/icons';
|
||||
|
||||
type MapMarkerProps = {
|
||||
childName: string;
|
||||
avatar: string;
|
||||
avatar: string | null
|
||||
deviceName: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
timestamp: Date;
|
||||
};
|
||||
const fillBlueOptions = { fillColor: 'blue' };
|
||||
const fillRedOptions = { fillColor: 'red' };
|
||||
|
||||
const _getIcon = (icon: string) => {
|
||||
const _getAvatarImage = (avatar: string | null) => avatar ?? '/img/default-avatar.png';
|
||||
const _getIcon = (icon: string | null) => {
|
||||
return L.icon({
|
||||
iconUrl: icon,
|
||||
iconUrl: _getAvatarImage(icon),
|
||||
iconSize: [24, 24],
|
||||
});
|
||||
};
|
||||
@@ -40,27 +37,31 @@ const MapMarker: React.FC<MapMarkerProps> = (
|
||||
>
|
||||
<Popup>
|
||||
<Card className="w-full max-w-md p-6 grid gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar className="h-16 w-16">
|
||||
<AvatarImage src={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">on {deviceName}</div>
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-4">
|
||||
<Avatar className="h-16 w-16">
|
||||
<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">on {deviceName}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Last seen - {timestamp.toLocaleDateString() + ' ' + timestamp.toLocaleTimeString()}
|
||||
</div>
|
||||
<div className="flex justify-end gap-4">
|
||||
</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="" href="#">
|
||||
<Icons.message className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link className="text-blue-700" href="#">
|
||||
<Icons.call className="h-6 w-6" />
|
||||
</Link>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
{/*<Card>*/}
|
||||
{/* <CardHeader>*/}
|
||||
|
||||
@@ -3,7 +3,7 @@ import DeviceModel from './device';
|
||||
export default interface ChildModel {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
avatar: string | null;
|
||||
devices: DeviceModel[];
|
||||
// recentLocations: Location[];
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import PingModel from '@/lib/models/ping';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export const getInitials = (name: string) => {
|
||||
return name?.match(/(\b\S)?/g).join('');
|
||||
};
|
||||
export const getInitials = (name: string) => name?.match(/(\b\S)?/g)?.join('') ?? '';
|
||||
|
||||
export const getLatestPing = (pings: PingModel[]): PingModel =>
|
||||
pings.reduce((ping, current) => (ping.timestamp > current.timestamp ? ping : current));
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { z } from "zod";
|
||||
import { z } from 'zod';
|
||||
|
||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||
import { child } from "@/server/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { createTRPCRouter, protectedProcedure } from '@/server/api/trpc';
|
||||
import { child } from '@/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export const childRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.input(z.object({ name: z.string().min(1) }))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
console.log("Child", "Create", ctx.session);
|
||||
console.log('Child', 'Create', ctx.session);
|
||||
const c = {
|
||||
parentId: ctx.session.user.id,
|
||||
name: input.name,
|
||||
|
||||
Reference in New Issue
Block a user