Repository navigation
VIBE-228 Approve Media Application #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 26 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
076d652
WIP
junaidiqbalmoj 99b2bca
Merge master into vibe-228-approve-media-application
junaidiqbalmoj 665aeb8
Implement Media Application Approval
junaidiqbalmoj 1cb6a68
fix lint and type script issues
junaidiqbalmoj 12781f0
Merge master into vibe-228-approve-media-application
junaidiqbalmoj 882fbf5
Fix vitest version mismatch in notification package
junaidiqbalmoj 9da207d
Add TypeScript declarations for notifications-node-client
junaidiqbalmoj 102cfba
Add comprehensive tests for media application approval feature
junaidiqbalmoj 0f6df13
fix end to end tests
junaidiqbalmoj 0b4a272
fix end to end tests
junaidiqbalmoj 22734a9
fix end to end tests
junaidiqbalmoj 56f853e
fix build
junaidiqbalmoj dc693ce
trigger build
junaidiqbalmoj 6eee408
Trigger CI cache refresh
junaidiqbalmoj f2b6eee
Update yarn.lock to invalidate cache
junaidiqbalmoj ef974bd
fix build
junaidiqbalmoj 1fae8a0
fix build
junaidiqbalmoj b4b1fce
chore: trigger CI
junaidiqbalmoj 5df9d25
Update yarn.lock after package.json changes
junaidiqbalmoj 835221d
Merge master into vibe-228-approve-media-application
junaidiqbalmoj 17383c6
Update yarn.lock after merge
junaidiqbalmoj 17fa35c
fix: remove trailing space in media-applications index
junaidiqbalmoj 9885fb2
remove database field not required.
junaidiqbalmoj 586179b
Merge branch 'master' into vibe-228-approve-media-application
junaidiqbalmoj fb4d214
Merge branch 'vibe-228-approve-media-application' of https://github.c…
junaidiqbalmoj dcd1a5b
fix bug
junaidiqbalmoj 5071e52
fix build
junaidiqbalmoj bbe7888
fix build
junaidiqbalmoj 4a8e1f7
bug fixes
junaidiqbalmoj 8686700
fix end to end test
junaidiqbalmoj f48a0ae
merge master
junaidiqbalmoj a3b6694
Merge branch 'master' into vibe-228-approve-media-application
junaidiqbalmoj 0b9812e
Merge branch 'master' into vibe-228-approve-media-application
junaidiqbalmoj e437d13
fix e2e tests build
junaidiqbalmoj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
24 changes: 24 additions & 0 deletions
24
apps/postgres/prisma/migrations/20251201102249_media_application/migration.sql
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /* | ||
| Warnings: | ||
|
|
||
| - You are about to drop the column `full_name` on the `media_application` table. All the data in the column will be lost. | ||
| - You are about to drop the column `request_date` on the `media_application` table. All the data in the column will be lost. | ||
| - You are about to drop the column `status_date` on the `media_application` table. All the data in the column will be lost. | ||
| - You are about to alter the column `email` on the `media_application` table. The data in that column could be lost. The data in that column will be cast from `Text` to `VarChar(255)`. | ||
| - You are about to alter the column `employer` on the `media_application` table. The data in that column could be lost. The data in that column will be cast from `Text` to `VarChar(255)`. | ||
| - You are about to alter the column `status` on the `media_application` table. The data in that column could be lost. The data in that column will be cast from `Text` to `VarChar(20)`. | ||
| - Added the required column `name` to the `media_application` table without a default value. This is not possible if the table is not empty. | ||
|
|
||
| */ | ||
| -- AlterTable | ||
| ALTER TABLE "media_application" DROP COLUMN "full_name", | ||
| DROP COLUMN "request_date", | ||
| DROP COLUMN "status_date", | ||
| ADD COLUMN "applied_date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| ADD COLUMN "name" VARCHAR(255) NOT NULL, | ||
| ADD COLUMN "proof_of_id_path" VARCHAR(500), | ||
| ADD COLUMN "reviewed_by" VARCHAR(255), | ||
| ADD COLUMN "reviewed_date" TIMESTAMP(3), | ||
| ALTER COLUMN "email" SET DATA TYPE VARCHAR(255), | ||
| ALTER COLUMN "employer" SET DATA TYPE VARCHAR(255), | ||
| ALTER COLUMN "status" SET DATA TYPE VARCHAR(20); | ||
10 changes: 10 additions & 0 deletions
10
...sma/migrations/20251201160103_remove_reviewed_fields_from_media_application/migration.sql
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| /* | ||
| Warnings: | ||
|
|
||
| - You are about to drop the column `reviewed_by` on the `media_application` table. All the data in the column will be lost. | ||
| - You are about to drop the column `reviewed_date` on the `media_application` table. All the data in the column will be lost. | ||
|
|
||
| */ | ||
| -- AlterTable | ||
| ALTER TABLE "media_application" DROP COLUMN "reviewed_by", | ||
| DROP COLUMN "reviewed_date"; | ||
|
junaidiqbalmoj marked this conversation as resolved.
|
||
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| # VIBE-228: Manage Media Account Requests - Approve Application | ||
|
|
||
| ## Technical Approach | ||
|
|
||
| This feature extends the existing admin-pages module to handle media account request approvals. The implementation follows the established pattern in `libs/admin-pages` with route-based pages under `pages/media-applications/`. Database schema for `media_application` will be added to the main Prisma schema at `apps/postgres/prisma/schema.prisma` with fields for applicant details, status tracking, and Press ID file reference. | ||
|
|
||
| The approval workflow involves: (1) listing pending applications from the database, (2) displaying applicant details with file preview capability, (3) confirming approval via a yes/no radio form, and (4) showing success confirmation. File handling reuses the existing file upload infrastructure used in manual-upload, storing Press ID files temporarily and deleting them post-approval. Session storage will track multi-page form state during the confirmation flow. | ||
|
|
||
| Authorization restricts all pages to CTSC Admin users (USER_ROLES.INTERNAL_ADMIN_CTSC) using the existing requireRole middleware. The dashboard notification will query pending application counts on page load. Error handling covers database failures, missing files, and invalid application states with appropriate English and Welsh error messages. | ||
|
|
||
| ## Implementation Details | ||
|
|
||
| ### Database Schema | ||
| **File**: `apps/postgres/prisma/schema.prisma` | ||
| ```prisma | ||
| model MediaApplication { | ||
| id String @id @default(uuid()) @db.Uuid | ||
| name String @db.VarChar(255) | ||
| email String @db.VarChar(255) | ||
| employer String @db.VarChar(255) | ||
| proofOfIdPath String? @map("proof_of_id_path") @db.VarChar(500) | ||
| status String @default("PENDING") @db.VarChar(20) | ||
| appliedDate DateTime @default(now()) @map("applied_date") | ||
| reviewedDate DateTime? @map("reviewed_date") | ||
| reviewedBy String? @map("reviewed_by") @db.VarChar(255) | ||
|
|
||
| @@map("media_application") | ||
| } | ||
| ``` | ||
|
|
||
| ### Module Structure | ||
| **Location**: `libs/admin-pages/src/` | ||
|
|
||
| ``` | ||
| media-application/ | ||
| ├── model.ts # TypeScript interfaces | ||
| ├── queries.ts # Database queries | ||
| └── service.ts # Business logic (approval/rejection) | ||
|
|
||
| pages/ | ||
| ├── media-applications/ | ||
| │ ├── index.ts # GET: List pending applications | ||
| │ └── index.njk # Template: Application table | ||
| ├── media-applications/ | ||
| │ └── [id]/ | ||
| │ ├── index.ts # GET: Applicant details | ||
| │ ├── index.njk # Template: Details with approve/reject buttons | ||
| │ ├── approve.ts # GET/POST: Confirmation form | ||
| │ ├── approve.njk # Template: Yes/No radio form | ||
| │ ├── approved.ts # GET: Success page | ||
| │ └── approved.njk # Template: Success banner with details | ||
| ``` | ||
|
|
||
| ### Page Routes | ||
| | Page | URL | Controller | Template | | ||
| |------|-----|------------|----------| | ||
| | List applications | `/media-applications` | `pages/media-applications/index.ts` | `index.njk` | | ||
| | Applicant details | `/media-applications/:id` | `pages/media-applications/[id]/index.ts` | `index.njk` | | ||
| | Approve confirmation | `/media-applications/:id/approve` | `pages/media-applications/[id]/approve.ts` | `approve.njk` | | ||
| | Approved success | `/media-applications/:id/approved` | `pages/media-applications/[id]/approved.ts` | `approved.njk` | | ||
|
|
||
| ### Key Functions | ||
|
|
||
| **queries.ts** | ||
| - `getPendingApplications()` - Fetch all PENDING applications ordered by appliedDate | ||
| - `getApplicationById(id)` - Fetch single application with all details | ||
| - `updateApplicationStatus(id, status, reviewedBy)` - Update status and reviewed metadata | ||
| - `getPendingCount()` - Count of pending applications for dashboard notification | ||
|
|
||
| **service.ts** | ||
| - `approveApplication(id, reviewedBy)` - Approve application, delete file, update status | ||
| - `deleteProofOfIdFile(filePath)` - Remove file from temp storage | ||
|
|
||
| ### Dashboard Integration | ||
| **File**: `libs/admin-pages/src/pages/admin-dashboard/index.ts` | ||
|
|
||
| Add notification box conditionally when pending applications exist: | ||
| ```typescript | ||
| const pendingCount = await getPendingCount(); | ||
| res.render("admin-dashboard/index", { | ||
| pageTitle: lang.pageTitle, | ||
| tiles, | ||
| pendingCount, | ||
| hideLanguageToggle: true | ||
| }); | ||
| ``` | ||
|
|
||
| **File**: `libs/admin-pages/src/pages/admin-dashboard/index.njk` | ||
|
|
||
| Add notification box below page title: | ||
| ```html | ||
| {% if pendingCount > 0 %} | ||
| <div class="govuk-notification-banner govuk-notification-banner--important"> | ||
| <div class="govuk-notification-banner__content"> | ||
| <p>{{ notificationText | replace("x", pendingCount) }}</p> | ||
| <a href="/media-applications">{{ notificationLink }}</a> | ||
| </div> | ||
| </div> | ||
| {% endif %} | ||
| ``` | ||
|
|
||
| ### Authorization | ||
| All pages use: `requireRole([USER_ROLES.INTERNAL_ADMIN_CTSC])` | ||
|
|
||
| ### File Handling | ||
| - Press ID files stored in temp directory (reuse existing upload infrastructure) | ||
| - `getApplicationById()` returns `proofOfIdPath` for file download link | ||
| - File links open in new tab via `target="_blank"` with `rel="noopener noreferrer"` | ||
| - `deleteProofOfIdFile()` removes file from filesystem after approval | ||
|
|
||
| ## Error Handling & Edge Cases | ||
|
|
||
| ### Validation | ||
| - Application ID must be valid UUID | ||
| - Application must exist in database | ||
| - Application status must be PENDING (prevent duplicate approvals) | ||
| - Radio selection (Yes/No) is mandatory on confirmation page | ||
| - File path must exist before displaying download link | ||
|
|
||
| ### Error States | ||
| - **Database unavailable**: Display "Unable to load applicant details. Please try again later." | ||
| - **Application not found**: 404 with proper error page | ||
| - **File missing**: Show placeholder text "File not available" instead of link | ||
| - **No radio selected**: "Select yes or no before continuing." | ||
| - **Already approved/rejected**: Redirect to list with warning message | ||
| - **Invalid session state**: Clear session and redirect to list | ||
|
|
||
| ### Security | ||
| - File paths sanitized to prevent directory traversal | ||
| - User authentication verified on all routes via requireRole middleware | ||
| - File downloads only permitted for authenticated CTSC admins | ||
| - SQL injection prevented via Prisma parameterized queries | ||
|
|
||
| ### Accessibility | ||
| - All form fields have proper labels and error associations | ||
| - Tables use semantic HTML with scope attributes | ||
| - Success banners use `role="status"` for screen readers | ||
| - File links include `aria-label="Opens in new window"` | ||
| - Full keyboard navigation with visible focus states | ||
|
|
||
| ## Acceptance Criteria Mapping | ||
|
|
||
| | AC | Implementation | Location | | ||
| |----|----------------|----------| | ||
| | 1 | Dashboard tile already exists, notification box added conditionally | `admin-dashboard/index.ts`, `admin-dashboard/index.njk` | | ||
| | 2 | Pending count query displays notification with link | `media-application/queries.ts` (getPendingCount) | | ||
| | 3 | Table displays pending applications with View links | `media-applications/index.ts`, `index.njk` | | ||
| | 4 | Details page with file preview and action buttons | `media-applications/[id]/index.ts`, `index.njk` | | ||
| | 5 | Confirmation page with radio form and applicant details | `media-applications/[id]/approve.ts`, `approve.njk` | | ||
| | 6 | Success page with banner and next steps message | `media-applications/[id]/approved.ts`, `approved.njk` | | ||
| | 7 | Reject flow placeholder (future iteration) | Reject button links to `/media-applications/:id/reject` (not implemented) | | ||
| | 8 | Back links on all pages using GOV.UK pattern | All templates extend base-template with back link support | | ||
|
|
||
| ## CLARIFICATIONS NEEDED | ||
|
|
||
| 1. **Email Notifications**: Ticket mentions "applicant will be notified to confirm their details" - should this be implemented now or in a future iteration? If now, what email service/template should be used? | ||
|
|
||
| 2. **File Storage Location**: Where exactly is the "temp folder" for Press ID files? Is it the same location as manual-upload files? Should we use Azure Blob Storage or local filesystem? | ||
|
|
||
| 3. **Existing Account Check**: Success message says "If an account already exists, the applicant will be asked to sign in" - is there existing logic to check for duplicate accounts by email before creating? | ||
|
|
||
| 4. **User Account Creation**: Does approving an application automatically create a User record in the database, or is that handled by a separate system after the applicant confirms their details? | ||
|
|
||
| 5. **Audit Logging**: Should approval actions be logged in an audit table, or is the reviewedBy/reviewedDate field sufficient? | ||
|
|
||
| 6. **Search/Filter**: Should the pending applications table support search/filter by name or employer, or is simple date-ordered listing sufficient for v1? | ||
|
|
||
| 7. **Reject Workflow**: Should the Reject button be disabled/hidden for now, or link to a placeholder page with "Coming soon" message? | ||
|
|
||
| 8. **File Upload Process**: How do applicants upload Press ID files during account creation? Is that part of a separate ticket (the "create media application" flow mentioned in pre-conditions)? | ||
|
|
||
| 9. **Pagination**: Should the pending applications list support pagination if there are many applications, or load all at once? |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.