feat: Add document template management functionality
- Created migration script to add `docx_templates` table with necessary fields and indexes. - Implemented API routes for uploading, fetching, and deleting document templates. - Developed document generation feature using selected templates and project data. - Added UI components for template upload and listing, including a modal for document generation. - Integrated document generation into the project view page, allowing users to generate documents based on selected templates. - Enhanced error handling and user feedback for template operations.
This commit is contained in:
133
src/app/api/templates/route.js
Normal file
133
src/app/api/templates/route.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { writeFile, mkdir, unlink } from "fs/promises";
|
||||
import { existsSync } from "fs";
|
||||
import path from "path";
|
||||
import db from "@/lib/db";
|
||||
|
||||
const TEMPLATES_DIR = path.join(process.cwd(), "public", "templates");
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
const ALLOWED_TYPES = [
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
];
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const formData = await request.formData();
|
||||
const file = formData.get("file");
|
||||
const templateName = formData.get("templateName");
|
||||
const description = formData.get("description") || "";
|
||||
|
||||
if (!file || !templateName) {
|
||||
return NextResponse.json(
|
||||
{ error: "File and templateName are required" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate file
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
return NextResponse.json(
|
||||
{ error: "File size too large (max 10MB)" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!ALLOWED_TYPES.includes(file.type)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Only DOCX files are allowed" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Create templates directory
|
||||
if (!existsSync(TEMPLATES_DIR)) {
|
||||
await mkdir(TEMPLATES_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Generate unique filename
|
||||
const timestamp = Date.now();
|
||||
const sanitizedOriginalName = file.name.replace(/[^a-zA-Z0-9.-]/g, "_");
|
||||
const storedFilename = `${timestamp}_${sanitizedOriginalName}`;
|
||||
const filePath = path.join(TEMPLATES_DIR, storedFilename);
|
||||
const relativePath = `/templates/${storedFilename}`;
|
||||
|
||||
// Save file
|
||||
const bytes = await file.arrayBuffer();
|
||||
const buffer = Buffer.from(bytes);
|
||||
await writeFile(filePath, buffer);
|
||||
|
||||
// Save to database
|
||||
const stmt = db.prepare(`
|
||||
INSERT INTO docx_templates (
|
||||
template_name, description, original_filename, stored_filename,
|
||||
file_path, file_size, mime_type, created_by
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
const result = stmt.run(
|
||||
templateName,
|
||||
description,
|
||||
file.name,
|
||||
storedFilename,
|
||||
relativePath,
|
||||
file.size,
|
||||
file.type,
|
||||
null // TODO: Get from session when auth is implemented
|
||||
);
|
||||
|
||||
const newTemplate = {
|
||||
template_id: result.lastInsertRowid,
|
||||
template_name: templateName,
|
||||
description: description,
|
||||
original_filename: file.name,
|
||||
stored_filename: storedFilename,
|
||||
file_path: relativePath,
|
||||
file_size: file.size,
|
||||
mime_type: file.type,
|
||||
is_active: 1,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
return NextResponse.json(newTemplate, { status: 201 });
|
||||
|
||||
} catch (error) {
|
||||
console.error("Template upload error:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to upload template" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(request) {
|
||||
try {
|
||||
const templates = db.prepare(`
|
||||
SELECT
|
||||
template_id,
|
||||
template_name,
|
||||
description,
|
||||
original_filename,
|
||||
stored_filename,
|
||||
file_path,
|
||||
file_size,
|
||||
mime_type,
|
||||
is_active,
|
||||
created_at,
|
||||
created_by,
|
||||
updated_at
|
||||
FROM docx_templates
|
||||
WHERE is_active = 1
|
||||
ORDER BY created_at DESC
|
||||
`).all();
|
||||
|
||||
return NextResponse.json(templates);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching templates:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to fetch templates" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user