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 Dashboard → Staff 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