Feature/drafts and email automation - #9
Merged
Conversation
✅ Deploy Preview for leddger-ai ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
This PR introduces a drafts workflow for template builders: templates can be saved as private drafts first, then activated later with an explicit expiration time via a new Drafts dashboard, enabling generation of public form links.
Changes:
- Added a Drafts dashboard UI to list drafts, schedule expiration, activate drafts, and copy public links.
- Updated template builders to “Save as Draft” (no link generation in-builder) and show a success overlay animation.
- Updated backend draft model/endpoints to support
draft -> active -> expiredstatus and activation-time expiration.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/pages/TemplateBuilder.css | Adds styling for a success overlay/video animation. |
| src/pages/TeamTemplateBuilder.jsx | Removes in-builder link generation UI; adds success overlay on save. |
| src/pages/StudentTemplateBuilder.jsx | Adds authenticated draft saving + success overlay and “Save Form” UI block. |
| src/pages/EmployeeTemplateBuilder.jsx | Adds authenticated draft saving + success overlay and “Save Form” UI block. |
| src/pages/DraftsView.jsx | New drafts management UI (list, activation scheduler, link generation/copy). |
| src/pages/DraftsView.css | Styling for the Drafts dashboard split-pane layout and components. |
| src/App.jsx | Adds “Drafts” + “Sent Forms” tabs and a public route for /form/:title/:draftId. |
| server/models/FormDraft.js | Adjusts schema to allow expiresAt: null and default status: draft. |
| server/index.js | Refactors draft creation + adds GET drafts and activation endpoint; blocks inactive public forms. |
| docs/feature-drafts-and-email.md | New feature documentation (needs updates for the new flow + spelling). |
| docs/drafts-dashboard-architecture.md | New architecture doc for drafts dashboard (minor layout correction needed). |
Suppressed comments (5)
docs/feature-drafts-and-email.md:20
- The Team Template Builder section describes the old "Save & Generate Link" + expiration dropdown flow, but the code now saves drafts without generating links and moves activation/scheduling to DraftsView. Updating this section will prevent onboarding confusion.
- Introduced a **"Save & Generate Link"** control panel.
- Allows recruiters to set an expiration period (`24 Hours`, `3 Days`, `7 Days`).
- **Draft Creation Flow**:
1. Compiles the current UI configuration (`toggles`, `selectedDepartments`, `leadRestriction`, etc.) into a config object.
2. Sends an authenticated `POST` request to `/api/drafts` with the configuration and expiration time.
3. Receives a `draftId` and generates a public-facing URL: `/form/[Title]/[draftId]`.
src/pages/DraftsView.jsx:60
- handleActivateDraft uses an optional token lookup, but still sends the request even if token is undefined, resulting in an invalid Authorization header and a confusing failure. Guard for a missing token before calling the activate endpoint, and avoid hardcoding localhost in the URL.
const token = await auth.currentUser?.getIdToken();
const res = await fetch(`http://localhost:5000/api/drafts/${selectedDraft.draftId}/activate`, {
method: 'PUT',
server/index.js:85
- POST /api/drafts relies on Mongoose validation for missing title/config, which turns a client error into a 500. Return a 400 when required fields are missing so API consumers get actionable feedback.
const { title, config } = req.body;
const draftId = uuidv4();
const newDraft = await FormDraft.create({
src/pages/StudentTemplateBuilder.jsx:122
- The success animation asset path is misspelled ("/Sucess.webm"). It works only as long as the public asset keeps the same typo, which is easy to break later. Consider renaming the asset to "/Success.webm" and updating all references.
{showSuccess && (
<div className="success-overlay">
<video src="/Sucess.webm" autoPlay muted className="success-video" />
</div>
src/pages/EmployeeTemplateBuilder.jsx:122
- The success animation asset path is misspelled ("/Sucess.webm"). It works only as long as the public asset keeps the same typo, which is easy to break later. Consider renaming the asset to "/Success.webm" and updating all references.
{showSuccess && (
<div className="success-overlay">
<video src="/Sucess.webm" autoPlay muted className="success-video" />
</div>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+169
to
+172
| {showSuccess && ( | ||
| <div className="success-overlay"> | ||
| <video src="/Sucess.webm" autoPlay muted className="success-video" /> | ||
| </div> |
Comment on lines
+115
to
+119
| <div | ||
| key={draft.draftId} | ||
| className={`draft-card ${selectedDraft?.draftId === draft.draftId ? 'selected' : ''}`} | ||
| onClick={() => handleSelectDraft(draft)} | ||
| > |
Comment on lines
+121
to
+135
| const { expiresAt } = req.body; | ||
|
|
||
| if (!expiresAt) { | ||
| return res.status(400).json({ error: 'expiresAt is required' }); | ||
| } | ||
|
|
||
| const draft = await FormDraft.findOne({ draftId: req.params.draftId, recruiterId: req.user.uid }); | ||
|
|
||
| if (!draft) { | ||
| return res.status(404).json({ error: 'Draft not found' }); | ||
| } | ||
|
|
||
| draft.expiresAt = new Date(expiresAt); | ||
| draft.status = 'active'; | ||
| await draft.save(); |
| @@ -0,0 +1,65 @@ | |||
| # Feature: Drafts, Temporary Links, & Email Automation | |||
|
|
|||
| This document outlines the architecture, data models, and logic implemented to support the creation of form drafts, temporary public links, and automated email notifications in the Leddger AI platform. | |||
| - The UI simply has a "Save as Draft" button which persists the toggles. | ||
|
|
||
| ### Drafts Dashboard (`src/pages/DraftsView.jsx`) | ||
| - **Split-Pane Layout**: A modern white-and-grey UI featuring a list of templates on the right, and an active configuration screen on the left. |
Comment on lines
+48
to
+51
| const token = await auth.currentUser.getIdToken(); | ||
|
|
||
| const response = await fetch('http://localhost:5000/api/drafts', { | ||
| method: 'POST', |
Comment on lines
+48
to
+51
| const token = await auth.currentUser.getIdToken(); | ||
|
|
||
| const response = await fetch('http://localhost:5000/api/drafts', { | ||
| method: 'POST', |
Comment on lines
+21
to
+25
| const token = await auth.currentUser?.getIdToken(); | ||
| if (!token) return; | ||
|
|
||
| const res = await fetch('http://localhost:5000/api/drafts', { | ||
| headers: { |
Comment on lines
+184
to
+190
| value={`http://localhost:5173/form/${encodeURIComponent(selectedDraft.title)}/${selectedDraft.draftId}`} | ||
| readOnly | ||
| /> | ||
| <button | ||
| className="copy-btn" | ||
| onClick={() => navigator.clipboard.writeText(`http://localhost:5173/form/${encodeURIComponent(selectedDraft.title)}/${selectedDraft.draftId}`)} | ||
| > |
Comment on lines
+156
to
+158
| if (draft.status === 'draft' || !draft.expiresAt) { | ||
| return res.status(403).json({ error: 'This form link is not yet active.' }); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.