Skip to main content

Staff Documents

This guide covers document management for staff members, including uploading files, document types, verification workflows, and compliance tracking.


Overview

Document Management Purpose

Staff documents in Shyfts support:

  1. Compliance - Track required certifications and qualifications
  2. HR Records - Maintain employment contracts and policies
  3. Training - Monitor professional development documents
  4. Tax & Payroll - Store P45, P60, and tax documents
  5. Identity - Keep verified ID documents on file

Two Document Systems

Shyfts has two document tables for different purposes:

TablePurposeScope
documentsCompany-wide documentsShared policies, templates
staff_documentsStaff-specific filesIndividual staff records

Staff Documents Database Structure

// Source: src/types/database.types.ts:2512-2560
staff_documents: {
Row: {
id: string
company_id: string
staff_id: string
name: string
document_type: string
file_path: string
file_size: number
mime_type: string
expiry_date: string | null
upload_date: string | null
is_confidential: boolean | null
notes: string | null
created_at: string | null
updated_at: string | null
}
}

Company Documents Structure

// Source: src/types/database.types.ts:650-701
documents: {
Row: {
id: string
company_id: string
staff_id: string | null
document_name: string
document_type: string
file_url: string
file_type: string | null
issue_date: string | null
expiry_date: string | null
is_verified: boolean | null
verified_by: string | null
verified_at: string | null
notes: string | null
created_at: string | null
updated_at: string | null
}
}

Accessing Staff Documents

From Staff Portal

Staff members access their documents via the Staff Portal:

Location: /staff/documents

// Source: src/app/staff/documents/page.tsx:35-70
export default function StaffDocumentsPage() {
const { user, loading } = useAuth()
const [documents, setDocuments] = useState<Document[]>([])
const [selectedCategory, setSelectedCategory] = useState<string>('all')

useEffect(() => {
const fetchDocuments = async () => {
try {
const response = await fetch('/api/staff/documents')
const data = await response.json()
setDocuments(data.documents || [])
} catch (error) {
console.error('Error fetching documents:', error)
}
}

if (user) {
fetchDocuments()
}
}, [user])
}

From Company Manager View

Company Managers access staff documents through the staff detail view:

  1. Navigate to DashboardStaff Management
  2. Click on a staff member
  3. Select the Documents tab (if available)

Document Types

Standard Categories

CategoryExamplesRetention
ContractsEmployment contract, amendmentsDuration of employment + 6 years
TaxP45, P60, P11D6 years
IdentityPassport, driving licence, visaWhile employed
CertificationsProfessional qualificationsWhile valid
TrainingCourse certificates, CPD records3 years
PoliciesSigned company policiesDuration of employment
PerformanceReviews, appraisals3 years
MedicalFit notes, occupational healthDuration of employment

Category Filtering

// Source: src/app/staff/documents/page.tsx:95-120
const categories = [
{ id: 'all', label: 'All Documents' },
{ id: 'contract', label: 'Contracts' },
{ id: 'tax', label: 'Tax Documents' },
{ id: 'certification', label: 'Certifications' },
{ id: 'training', label: 'Training' },
{ id: 'policy', label: 'Policies' },
{ id: 'other', label: 'Other' }
]

const filteredDocuments = selectedCategory === 'all'
? documents
: documents.filter(doc => doc.document_type === selectedCategory)

Uploading Documents

Upload Process

  1. Navigate to the staff member's profile
  2. Go to the Documents section
  3. Click Upload Document
  4. Select the file from your device
  5. Choose the document type
  6. Add any relevant notes
  7. Set expiry date if applicable
  8. Click Upload

File Validation

PropertyLimit
Maximum size10 MB
Allowed typesPDF, PNG, JPG, JPEG, DOC, DOCX
File nameMax 255 characters

Upload API

Endpoint: POST /api/documents

Request (multipart/form-data):

file: [binary file data]
staff_id: "uuid"
document_type: "contract"
document_name: "Employment Contract 2025"
expiry_date: "2026-01-13" (optional)
notes: "Updated contract with salary revision" (optional)

Response:

{
"success": true,
"data": {
"id": "doc-uuid",
"document_name": "Employment Contract 2025",
"document_type": "contract",
"file_url": "https://storage.supabase.co/...",
"created_at": "2025-01-13T10:00:00Z"
}
}

Document Verification

Verification Workflow

For documents requiring verification (e.g., ID, certifications):

  1. Upload - Document uploaded by staff or manager
  2. Pending - Document awaits verification
  3. Review - Manager reviews document authenticity
  4. Verified/Rejected - Status updated with notes

Verification Fields

FieldPurpose
is_verifiedBoolean verification status
verified_byStaff ID of verifier
verified_atTimestamp of verification
notesVerification notes/comments

Verification API

Endpoint: PATCH /api/documents/[id]

Request Body:

{
"is_verified": true,
"verified_by": "manager-staff-id",
"notes": "Original document sighted and verified"
}

Viewing Documents

Document List Display

// Source: src/app/staff/documents/page.tsx:160-200
{documents.map((doc) => (
<div key={doc.id} className="card-glass p-4 rounded-lg">
<div className="flex items-start justify-between">
<div className="flex items-center gap-3">
<FileText className="w-8 h-8 text-accent-coral" />
<div>
<h3 className="font-medium text-primary-text">
{doc.document_name}
</h3>
<p className="text-sm text-secondary-text">
{doc.document_type}Uploaded {formatDate(doc.created_at)}
</p>
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => handleDownload(doc.id)}
className="btn-icon"
>
<Download className="w-4 h-4" />
</button>
<button
onClick={() => handleDelete(doc.id)}
className="btn-icon text-red-400"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
</div>
</div>
))}

Document Information Displayed

FieldFormat
NameDocument name
TypeCategory badge
Upload DateDD/MM/YYYY
Expiry DateDD/MM/YYYY (if set)
StatusVerified/Pending/Expired badge
ActionsDownload, Delete buttons

Downloading Documents

Download API

Endpoint: GET /api/documents/download/[id]

// Source: src/app/api/documents/download/[id]/route.ts:15-45
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const supabase = await createServerClient()

// Fetch document metadata
const { data: document, error } = await supabase
.from('documents')
.select('*')
.eq('id', params.id)
.single()

if (error || !document) {
return NextResponse.json(
{ error: 'Document not found' },
{ status: 404 }
)
}

// Generate signed URL for download
const { data: signedUrl } = await supabase.storage
.from('documents')
.createSignedUrl(document.file_path, 60)

return NextResponse.json({ url: signedUrl.signedUrl })
}

Signed URL Security

Downloads use time-limited signed URLs:

PropertyValue
URL Validity60 seconds
Access ControlRLS enforced
Audit TrailDownload logged

Expiry Tracking

Expiry Date Management

Documents with expiry dates are tracked for compliance:

// Check for expiring documents
const expiringDocs = documents.filter(doc => {
if (!doc.expiry_date) return false
const expiryDate = new Date(doc.expiry_date)
const warningDate = new Date()
warningDate.setDate(warningDate.getDate() + 30)
return expiryDate <= warningDate
})

Expiry Status Display

StatusConditionDisplay
Valid> 30 days until expiryGreen badge
Expiring Soon≤ 30 days until expiryAmber badge
ExpiredPast expiry dateRed badge
No ExpiryNo expiry date setNo badge

Expiry Notifications

When documents are expiring:

  1. Dashboard Alert - Banner notification for managers
  2. Staff Notification - Reminder to upload renewed document
  3. Email Reminder - Automated email 30 days before expiry

Confidential Documents

Confidentiality Flag

The is_confidential field marks sensitive documents:

// Source: src/types/database.types.ts:2521
is_confidential: boolean | null

Access Control

Document TypeWho Can View
StandardStaff member, Company Manager
ConfidentialCompany Manager only

Marking as Confidential

When uploading or editing:

{
"is_confidential": true,
"notes": "Contains salary information"
}

Deleting Documents

Soft Delete

Documents are soft-deleted to maintain audit trails:

Endpoint: DELETE /api/documents/[id]

// Source: src/app/api/documents/[id]/route.ts:45-70
export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
const supabase = await createServerClient()

// Verify ownership/permissions
const { data: { user } } = await supabase.auth.getUser()

// Delete document record
const { error } = await supabase
.from('documents')
.delete()
.eq('id', params.id)

if (error) {
return NextResponse.json(
{ error: 'Failed to delete document' },
{ status: 500 }
)
}

// Also delete from storage
await supabase.storage
.from('documents')
.remove([document.file_path])

return NextResponse.json({ success: true })
}

Deletion Confirmation

const handleDelete = async (docId: string) => {
if (!confirm('Are you sure you want to delete this document? This cannot be undone.')) {
return
}

try {
await fetch(`/api/documents/${docId}`, { method: 'DELETE' })
// Refresh document list
refreshDocuments()
} catch (error) {
console.error('Error deleting document:', error)
}
}

Storage Configuration

Supabase Storage

Documents are stored in Supabase Storage:

BucketPurpose
documentsAll document files

File Path Structure

documents/
├── {company_id}/
│ ├── company/ # Company-wide documents
│ │ └── policies/
│ └── staff/
│ └── {staff_id}/ # Staff-specific documents
│ ├── contracts/
│ ├── certifications/
│ └── tax/

Storage Policies

PolicyRule
UploadAuthenticated users only
DownloadSame company or owner
DeleteManager or document owner

Compliance Requirements

GDPR Considerations

RequirementImplementation
Right to AccessStaff can view all their documents
Right to ErasureDelete function available
Data MinimisationOnly necessary documents stored
SecurityEncrypted storage, signed URLs

Retention Periods

Document TypeRetention Period
Employment contractsDuration + 6 years
Tax records6 years
Training records3 years minimum
Disciplinary recordsDuration of warning
Medical recordsDuration of employment

Automatic Cleanup

Documents past retention can be flagged for review:

-- Find documents past retention
SELECT * FROM staff_documents
WHERE created_at < NOW() - INTERVAL '6 years'
AND document_type = 'tax'

API Reference

List Staff Documents

Endpoint: GET /api/staff/documents

Response:

{
"success": true,
"documents": [
{
"id": "doc-uuid",
"document_name": "Employment Contract",
"document_type": "contract",
"file_url": "...",
"expiry_date": null,
"is_verified": true,
"created_at": "2025-01-13T10:00:00Z"
}
]
}

Get Single Document

Endpoint: GET /api/documents/[id]

Update Document

Endpoint: PATCH /api/documents/[id]

Request Body:

{
"document_name": "Updated Name",
"expiry_date": "2026-01-13",
"notes": "Updated notes"
}

Best Practices

Document Management

  1. Consistent naming - Use clear, descriptive names
  2. Correct categorisation - Choose appropriate document type
  3. Set expiry dates - For all time-limited documents
  4. Regular audits - Review document completeness quarterly
  5. Prompt verification - Verify ID documents promptly

Security

  1. Mark confidential - Flag sensitive documents appropriately
  2. Limit access - Only grant access to those who need it
  3. Audit downloads - Monitor document access patterns
  4. Secure disposal - Follow retention policies

Compliance

  1. Collect required docs - Ensure all legally required documents are on file
  2. Track expiry - Monitor certification expiry dates
  3. Maintain records - Keep verification audit trail
  4. Follow retention - Delete documents per retention policy

Troubleshooting

Upload Issues

IssueSolution
File too largeCompress or split document
Invalid file typeConvert to PDF
Upload failedCheck network connection, retry
Permission deniedVerify user has upload rights

Access Issues

IssueSolution
Document not foundCheck document ID and permissions
Download timeoutSigned URL may have expired, retry
Cannot view confidentialRequest manager access


Source Files:

  • src/types/database.types.ts - Document type definitions
  • src/app/staff/documents/page.tsx - Staff documents page
  • src/app/api/documents/route.ts - Document upload API
  • src/app/api/documents/[id]/route.ts - Document management API
  • src/app/api/documents/download/[id]/route.ts - Download API