web: fetch audit events and generate comments

This commit is contained in:
Quaternions 2025-04-11 18:45:46 -07:00
parent 5846e92924
commit 3c0f3a7001
Signed by: Quaternions
GPG Key ID: D0DF5964F79AC131
3 changed files with 128 additions and 9 deletions
web/src/app

@ -52,7 +52,7 @@ function LeaveAComment() {
)
}
export default function Comments(stats: CommentersProps) {
export function Comments(stats: CommentersProps) {
return (<>
<section className="comments">
{stats.comments_data.comments.length===0
@ -66,5 +66,6 @@ export default function Comments(stats: CommentersProps) {
}
export {
type CreatorAndReviewStatus
type CreatorAndReviewStatus,
type Comment,
}

@ -5,7 +5,8 @@ import type { CreatorAndReviewStatus } from "./_comments";
import { MapImage } from "./_mapImage";
import { useParams } from "next/navigation";
import ReviewButtons from "./_reviewButtons";
import Comments from "./_comments";
import { Comments, Comment } from "./_comments";
import { AuditEvent, decodeAuditEvent } from "@/app/ts/AuditEvent";
import Webpage from "@/app/_components/webpage";
import Link from "next/link";
import { useState, useEffect } from "react";
@ -62,19 +63,35 @@ function TitleAndComments(stats: CreatorAndReviewStatus) {
}
export default function MapfixInfoPage() {
const dynamicId = useParams<{mapfixId: string}>()
const { mapfixId } = useParams < { mapfixId: string } >()
const [mapfix, setMapfix] = useState<MapfixInfo | null>(null)
const [auditEvents, setAuditEvents] = useState<AuditEvent[]>([])
useEffect(() => { // needs to be client sided since server doesn't have a session, nextjs got mad at me for exporting an async function: (https://nextjs.org/docs/messages/no-async-client-component)
async function getMapfix() {
const res = await fetch(`/api/mapfixes/${dynamicId.mapfixId}`)
const res = await fetch(`/api/mapfixes/${mapfixId}`)
if (res.ok) {
setMapfix(await res.json())
}
}
getMapfix()
}, [dynamicId.mapfixId])
async function getAuditEvents() {
const res = await fetch(`/api/mapfixes/${mapfixId}/audit-events?Page=1&Limit=100`)
if (res.ok) {
setAuditEvents(await res.json())
}
}
getMapfix()
getAuditEvents()
}, [mapfixId])
const comments:Comment[] = auditEvents.map((auditEvent) => {
return {
date: auditEvent.created_at,
name: auditEvent.user.toString(),
comment: decodeAuditEvent(auditEvent).event_data.toString(),
}
})
if (!mapfix) {
return <Webpage>
@ -85,7 +102,7 @@ export default function MapfixInfoPage() {
<Webpage>
<main className="map-page-main">
<section className="review-section">
<RatingArea mapfixId={dynamicId.mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
<RatingArea mapfixId={mapfixId} mapfixStatus={mapfix.StatusID} mapfixSubmitter={mapfix.Submitter} mapfixAssetId={mapfix.AssetID} mapfixTargetAssetId={mapfix.TargetAssetID} />
<TitleAndComments
name={mapfix.DisplayName}
creator={mapfix.Creator}
@ -94,7 +111,7 @@ export default function MapfixInfoPage() {
submitter={mapfix.Submitter}
target_asset_id={mapfix.TargetAssetID}
description={mapfix.Description}
comments={[]}
comments={comments}
/>
</section>
</main>

@ -0,0 +1,101 @@
// Shared audit event types
export enum AuditEventType {
Action = 0,
Comment = 1,
ChangeModel = 2,
ChangeValidatedModel = 3,
ChangeDisplayName = 4,
ChangeCreator = 5,
Error = 6,
}
// Discriminated union types for each event
export type AuditEventData =
| { event_type: AuditEventType.Action; event_data: AuditEventDataAction }
| { event_type: AuditEventType.Comment; event_data: AuditEventDataComment }
| { event_type: AuditEventType.ChangeModel; event_data: AuditEventDataChangeModel }
| { event_type: AuditEventType.ChangeValidatedModel; event_data: AuditEventDataChangeValidatedModel; }
| { event_type: AuditEventType.ChangeDisplayName; event_data: AuditEventDataChangeName; }
| { event_type: AuditEventType.ChangeCreator; event_data: AuditEventDataChangeName; }
| { event_type: AuditEventType.Error; event_data: AuditEventDataError };
// Concrete data interfaces
export interface AuditEventDataAction {
target_status: number;
}
export interface AuditEventDataComment {
comment: string;
}
export interface AuditEventDataChangeModel {
old_model_id: number;
old_model_version: number;
new_model_id: number;
new_model_version: number;
}
export interface AuditEventDataChangeValidatedModel {
validated_model_id: number;
validated_model_version: number;
}
export interface AuditEventDataChangeName {
old_name: string;
new_name: string;
}
export interface AuditEventDataError {
error: string;
}
// Full audit event type (mirroring the Go struct)
export interface AuditEvent {
id: number;
created_at: string; // ISO string, can convert to Date if needed
user: number;
resource_type: string; // Assuming this is a string enum or similar
resource_id: number;
event_type: AuditEventType;
event_data: unknown; // You'll decode this into a specific AuditEventData based on `event_type`
}
// Optional: decode function to parse event_data into strongly-typed structure
export function decodeAuditEvent(event: AuditEvent): AuditEventData {
switch (event.event_type) {
case AuditEventType.Action:
return {
event_type: event.event_type,
event_data: event.event_data as AuditEventDataAction,
};
case AuditEventType.Comment:
return {
event_type: event.event_type,
event_data: event.event_data as AuditEventDataComment,
};
case AuditEventType.ChangeModel:
return {
event_type: event.event_type,
event_data: event.event_data as AuditEventDataChangeModel,
};
case AuditEventType.ChangeValidatedModel:
return {
event_type: event.event_type,
event_data:
event.event_data as AuditEventDataChangeValidatedModel,
};
case AuditEventType.ChangeDisplayName:
case AuditEventType.ChangeCreator:
return {
event_type: event.event_type,
event_data: event.event_data as AuditEventDataChangeName,
};
case AuditEventType.Error:
return {
event_type: event.event_type,
event_data: event.event_data as AuditEventDataError,
};
default:
throw new Error(`Unknown event_type: ${event.event_type}`);
}
}