feat: implement label management with sidebar, hacker theme, and Docker support
- Add label selection on chart with visual highlight (size 2x, color change) - Implement keyboard delete handler (Delete/Backspace keys) - Add comprehensive label management sidebar with: - Collapsible label annotations section - Search by timestamp - Filter by type (Break Up, Break Down, All) - Individual delete buttons - Count display - Click to select/highlight on chart - Transform UI with hacker theme: - Matrix green (#00ff41) on dark background (#0a0e0a) - Monospace font (JetBrains Mono) - Glow effects on button hover and active states - Custom scrollbar styling - Terminal-inspired aesthetic - Add Docker deployment: - Multi-stage Dockerfile with standalone output - docker-compose.yml with volume persistence - Non-root user (nextjs) for security - Health check endpoint integration - Tailwind and CSS enhancements: - Custom colors (matrix, matrixDim, neonRed, etc.) - Glow box shadows and animations - Selection and scrollbar styling
This commit is contained in:
parent
74b84073a9
commit
a1fa86fe55
14 changed files with 509 additions and 42 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { annotations } from '@/lib/db/schema';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
|
||||
// GET all annotations
|
||||
export async function GET() {
|
||||
|
|
@ -66,3 +67,42 @@ export async function POST(request: NextRequest) {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE annotations with bulk operations
|
||||
export async function DELETE(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const type = searchParams.get('type');
|
||||
const all = searchParams.get('all');
|
||||
|
||||
let result;
|
||||
|
||||
if (all === 'true') {
|
||||
// Delete all annotations
|
||||
result = await db.delete(annotations).returning();
|
||||
} else if (type) {
|
||||
// Delete by type(s)
|
||||
const types = type.split(',').map((t) => t.trim());
|
||||
result = await db
|
||||
.delete(annotations)
|
||||
.where(inArray(annotations.label_type, types))
|
||||
.returning();
|
||||
} else {
|
||||
// No filter specified
|
||||
return NextResponse.json(
|
||||
{ error: 'Specify type or all parameter for bulk delete' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
deleted: result.length,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{ error: error.message || 'Failed to delete annotations' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
41
src/app/api/health/route.ts
Normal file
41
src/app/api/health/route.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { searchParams } = request.nextUrl;
|
||||
const checkDb = searchParams.get('check');
|
||||
|
||||
const response: any = {
|
||||
status: 'ok',
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
if (checkDb === 'db') {
|
||||
try {
|
||||
// Test database connectivity with a simple query
|
||||
await db.execute('SELECT 1');
|
||||
response.database = 'ok';
|
||||
} catch (dbError: any) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
status: 'error',
|
||||
database: 'failed',
|
||||
message: dbError.message || 'Database check failed',
|
||||
},
|
||||
{ status: 503 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(response);
|
||||
} catch (error: any) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
status: 'error',
|
||||
message: error.message || 'Health check failed',
|
||||
},
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue