From 617f8160b2945f315a6b7752011df0acc079be3b Mon Sep 17 00:00:00 2001 From: Elijah Date: Sun, 24 May 2026 19:30:17 -0700 Subject: [PATCH] Fix UI issues and add Pins and Templates --- backend/create_blanks.go | 7 ++ backend/handlers/onlyoffice.go | 61 ++++++++++ backend/main.go | 1 + backend/templates/apa.docx | 7 ++ backend/templates/resume.docx | 7 ++ frontend/src/components/FileExplorer.tsx | 88 ++++++++------ frontend/src/components/OfficeHome.tsx | 120 +++++++++++++++++-- frontend/src/components/OnlyOfficeEditor.tsx | 40 +++++-- frontend/src/lib/api.ts | 8 ++ frontend/tsconfig.tsbuildinfo | 1 + 10 files changed, 281 insertions(+), 59 deletions(-) create mode 100644 backend/create_blanks.go create mode 100644 backend/templates/apa.docx create mode 100644 backend/templates/resume.docx create mode 100644 frontend/tsconfig.tsbuildinfo diff --git a/backend/create_blanks.go b/backend/create_blanks.go new file mode 100644 index 0000000..ea0c318 --- /dev/null +++ b/backend/create_blanks.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Running...") +} diff --git a/backend/handlers/onlyoffice.go b/backend/handlers/onlyoffice.go index f6defff..52a98fd 100644 --- a/backend/handlers/onlyoffice.go +++ b/backend/handlers/onlyoffice.go @@ -166,6 +166,67 @@ func (h *OnlyOfficeHandler) CreateBlank(c *fiber.Ctx) error { }) } +// POST /api/onlyoffice/template +func (h *OnlyOfficeHandler) CreateFromTemplate(c *fiber.Ctx) error { + var req struct { + TemplateName string `json:"template"` // e.g., "apa.docx" + } + if err := c.BodyParser(&req); err != nil { + return c.Status(400).JSON(fiber.Map{"error": "invalid request"}) + } + + srcPath := filepath.Join("templates", req.TemplateName) + if _, err := os.Stat(srcPath); os.IsNotExist(err) { + return c.Status(404).JSON(fiber.Map{"error": "template not found"}) + } + + // Figure out a safe name in root directory + baseName := "Untitled Document" + ext := ".docx" + if strings.HasSuffix(req.TemplateName, ".pptx") { + baseName = "Untitled Presentation" + ext = ".pptx" + } + if req.TemplateName == "apa.docx" { + baseName = "APA Paper" + } else if req.TemplateName == "resume.docx" { + baseName = "Resume" + } + + finalPath := filepath.Join(h.Config.StorageDir, baseName+ext) + counter := 1 + for { + if _, err := os.Stat(finalPath); os.IsNotExist(err) { + break + } + finalPath = filepath.Join(h.Config.StorageDir, fmt.Sprintf("%s (%d)%s", baseName, counter, ext)) + counter++ + } + + // Copy the template file + srcFile, err := os.Open(srcPath) + if err != nil { + return c.Status(500).JSON(fiber.Map{"error": "failed to open template"}) + } + defer srcFile.Close() + + destFile, err := os.Create(finalPath) + if err != nil { + return c.Status(500).JSON(fiber.Map{"error": "failed to create destination file"}) + } + defer destFile.Close() + + if _, err := io.Copy(destFile, srcFile); err != nil { + return c.Status(500).JSON(fiber.Map{"error": "failed to copy template data"}) + } + + relativePath, _ := filepath.Rel(h.Config.StorageDir, finalPath) + return c.JSON(fiber.Map{ + "path": filepath.ToSlash(relativePath), + "name": filepath.Base(finalPath), + }) +} + // ListOfficeFiles returns all .docx or .pptx files across the entire storage directory. // GET /api/onlyoffice/files?type=docx|pptx func (h *OnlyOfficeHandler) ListOfficeFiles(c *fiber.Ctx) error { diff --git a/backend/main.go b/backend/main.go index 4948066..55452ce 100644 --- a/backend/main.go +++ b/backend/main.go @@ -193,6 +193,7 @@ func main() { // OnlyOffice protected.Post("/onlyoffice/create", onlyOfficeHandler.CreateBlank) + protected.Post("/onlyoffice/template", onlyOfficeHandler.CreateFromTemplate) protected.Post("/onlyoffice/sign", onlyOfficeHandler.SignConfig) protected.Get("/onlyoffice/files", onlyOfficeHandler.ListOfficeFiles) diff --git a/backend/templates/apa.docx b/backend/templates/apa.docx new file mode 100644 index 0000000..5bb59be --- /dev/null +++ b/backend/templates/apa.docx @@ -0,0 +1,7 @@ + + + + + + diff --git a/backend/templates/resume.docx b/backend/templates/resume.docx new file mode 100644 index 0000000..5bb59be --- /dev/null +++ b/backend/templates/resume.docx @@ -0,0 +1,7 @@ + + + + + + diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index 70fdc26..d3464be 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -1578,7 +1578,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
)} - {pathSegments.map((seg, i) => ( -
- {i > 0 && } - -
- ))} + {appMode === 'drive' ? ( + pathSegments.map((seg, i) => ( +
+ {i > 0 && } + +
+ )) + ) : ( + + {appMode === 'docs' ? 'Documents' : 'Presentations'} + + )} )} - {activeSection !== 'settings' && ( + {activeSection !== 'settings' && appMode === 'drive' && (
{!isMobile || mobileSearchOpen ? ( <> @@ -1799,31 +1805,35 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
-
- -
-
- {['name', 'size', 'date', 'type'].map(opt => ( - - ))} + {appMode === 'drive' && ( + <> +
+ +
+
+ {['name', 'size', 'date', 'type'].map(opt => ( + + ))} +
+
-
-
- + + + )} {activeSection === 'files' && ( @@ -207,7 +241,7 @@ export default function OfficeHome({ type, onFileSelect, onCreateBlank }: Office style={{ color: 'var(--color-text-primary)', wordBreak: 'break-word' }} title={file.name} > - {file.name} + {baseName} )}
@@ -225,16 +259,73 @@ export default function OfficeHome({ type, onFileSelect, onCreateBlank }: Office
Blank
+ {type === 'docs' && ( + <> +
+ + APA Paper +
+
+ + Resume +
+ + )}
+ {/* Pinned Documents */} + {pinnedFiles.length > 0 && ( +
+
+

Pinned {title.toLowerCase()}

+
+ {pinnedFiles.map(file => renderFileCard(file))} +
+
+
+ )} + {/* Recent Documents */}
@@ -285,8 +376,21 @@ export default function OfficeHome({ type, onFileSelect, onCreateBlank }: Office className="w-full px-4 py-2 text-left text-sm flex items-center gap-3 hover:bg-black/5 transition-colors" style={{ color: 'var(--color-text-primary)' }} onClick={() => { + handlePin(contextMenu.file); + setContextMenu(null); + }} + > + + {contextMenu.file.is_pinned || pinnedFiles.find(f => f.path === contextMenu.file.path) ? 'Unpin' : 'Pin'} + + )}