# Vis En France — Frontend Technical & Functional Specification
Browse files**Version**: 2.0
**Framework**: Next.js 15 (App Router)
**Last Updated**: December 2024
---
## Table of Contents
1. [Project Overview](#1-project-overview)
2. [Tech Stack & Dependencies](#2-tech-stack--dependencies)
3. [Environment Configuration](#3-environment-configuration)
4. [Project Structure](#4-project-structure)
5. [Routing & Pages](#5-routing--pages)
6. [Layout System](#6-layout-system)
7. [Navigation Architecture](#7-navigation-architecture)
8. [Component Specifications](#8-component-specifications)
9. [Advertising System](#9-advertising-system)
10. [Content Architecture](#10-content-architecture)
11. [Feature Flags System](#11-feature-flags-system)
12. [Internationalization (i18n)](#12-internationalization-i18n)
13. [State Management](#13-state-management)
14. [Custom Hooks](#14-custom-hooks)
15. [Authentication System](#15-authentication-system)
16. [Data Layer & Mock Patterns](#16-data-layer--mock-patterns)
17. [Type Definitions](#17-type-definitions)
18. [Styling Guidelines](#18-styling-guidelines)
19. [Accessibility Requirements](#19-accessibility-requirements)
20. [Performance Guidelines](#20-performance-guidelines)
21. [SEO & Meta](#21-seo--meta)
22. [Error Handling](#22-error-handling)
23. [Testing Strategy](#23-testing-strategy)
24. [Development Workflow](#24-development-workflow)
25. [Build Order & Milestones](#25-build-order--milestones)
---
## 1. Project Overview
### Purpose
**Vis En France** is a multilingual web platform providing comprehensive guidance for three distinct audiences navigating French administrative processes:
| Audience | French Slug | English Label | Description |
|----------|-------------|---------------|-------------|
| Students | `etudiants` | Students | Student visa procedures, scholarships, VLS-TS |
| Employees | `salarie` | Employees | Work permits, Passeport Talent, employment contracts |
| Naturalization | `naturalisations` | Naturalization | Citizenship process, eligibility, interviews |
### Core User Flows
1. **Audience Selection** → User selects their situation via top tabs
2. **Section Navigation** → User browses sub-categories via pill navigation
3. **Content Consumption** → User reads articles and guides
4. **Question Submission** → User asks questions via modal form
5. **Ad Interaction** → User views and clicks sponsor advertisements
### Key Features
- Tab-based audience switching with sub-category filtering
- Static guides (Overview, Steps, Documents) per audience
- Dynamic article/update feeds
- Rotating advertisement sidebars (desktop) and banners (mobile)
- Multilingual support (French/English)
- Mock visitor counter for MVP
- Feature flag controlled functionality
- User authentication with role-based access
---
## 2. Tech Stack & Dependencies
### Core Framework
| Package | Version | Purpose |
|---------|---------|---------|
| Next.js | 15.x | React framework with App Router |
| React | 18.3.x | UI library |
| TypeScript | 5.4.x | Type safety |
### Styling
| Package | Version | Purpose |
|---------|---------|---------|
| Tailwind CSS | 3.4.x | Utility-first CSS |
| tailwind-merge | 2.x | Class name conflict resolution |
| clsx | 2.x | Conditional class names |
### State & Data
| Package | Version | Purpose |
|---------|---------|---------|
| Zustand | 4.5.x | Global state management |
| next-intl | 3.20.x | Internationalization |
### UI Components
| Package | Version | Purpose |
|---------|---------|---------|
| Lucide React | 0.400.x | Icon library |
| date-fns | 3.6.x | Date formatting |
| react-google-recaptcha | 3.x | Form protection |
### Development
| Package | Version | Purpose |
|---------|---------|---------|
| ESLint | 8.x | Code linting |
| Prettier | 3.x | Code formatting |
| Playwright | 1.x | E2E testing |
| React Testing Library | 14.x | Component testing |
---
## 3. Environment Configuration
### Environment Variables
Create `.env.local` with the following variables:
```env
# API Configuration
NEXT_PUBLIC_API_BASE=/api
NEXT_PUBLIC_SITE_URL=http://localhost:3000
# Internationalization
NEXT_PUBLIC_DEFAULT_LOCALE=fr
NEXT_PUBLIC_SUPPORTED_LOCALES=fr,en
# Advertising
NEXT_PUBLIC_AD_ROTATION_MS=15000
NEXT_PUBLIC_AD_SLOTS_PER_SIDE=6
# Authentication
NEXT_PUBLIC_JWT_EXPIRY_DAYS=7
# reCAPTCHA
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=your_site_key_here
RECAPTCHA_SECRET_KEY=your_secret_key_here
# Feature Defaults (overridden by API)
NEXT_PUBLIC_ADS_ENABLED=true
NEXT_PUBLIC_FAQ_ENABLED=true
```
### Variable Naming Convention
- `NEXT_PUBLIC_*` — Exposed to browser, use for client-side configuration
- No prefix — Server-only, use for secrets and API keys
---
## 4. Project Structure
```
frontend/
├── src/
│ ├── app/ # Next.js App Router
│ │ ├── layout.tsx # Root layout (html, body, providers)
│ │ ├── page.tsx # Home page (/)
│ │ ├── [locale]/ # Locale-wrapped routes
│ │ │ ├── layout.tsx # Locale provider wrapper
│ │ │ └── page.tsx # Localized home
│ │ ├── articles/
│ │ │ └── [slug]/
│ │ │ └── page.tsx # Article detail
│ │ ├── guides/
│ │ │ └── [audience]/
│ │ │ ├── page.tsx # Guide overview
│ │ │ ├── steps/page.tsx # Step-by-step timeline
│ │ │ └── documents/page.tsx# Document checklist
│ │ ├── faq/
│ │ │ └── page.tsx # Global FAQ
│ │ ├── (admin)/
│ │ │ └── dashboard/
│ │ │ ├── layout.tsx # Admin layout
│ │ │ ├── page.tsx # Dashboard overview
│ │ │ ├── settings/page.tsx # Site settings
│ │ │ ├── articles/page.tsx # Article management
│ │ │ ├── ads/page.tsx # Ad management
│ │ │ └── users/page.tsx # User management
│ │ └── (auth)/
│ │ ├── login/page.tsx # Login page
│ │ └── register/page.tsx # Registration page
│ │
│ ├── components/
│ │ ├── ui/ # Base UI primitives
│ │ │ ├── Button.tsx
│ │ │ ├── Input.tsx
│ │ │ ├── Textarea.tsx
│ │ │ ├── Modal.tsx
│ │ │ ├── Card.tsx
│ │ │ ├── Badge.tsx
│ │ │ ├── Skeleton.tsx
│ │ │ ├── Toast.tsx
│ │ │ └── index.ts
│ │ ├── layout/ # Layout components
│ │ │ ├── Header.tsx
│ │ │ ├── MainLayout.tsx
│ │ │ ├── AdminLayout.tsx
│ │ │ ├── Footer.tsx
│ │ │ └── index.ts
│ │ ├── navigation/ # Navigation components
│ │ │ ├── TabNavigation.tsx
│ │ │ ├── SubNavigation.tsx
│ │ │ ├── LanguageSwitcher.tsx
│ │ │ ├── VisitorCounter.tsx
│ │ │ ├── Breadcrumbs.tsx
│ │ │ └── index.ts
│ │ ├── ads/ # Advertising components
│ │ │ ├── AdCard.tsx
│ │ │ ├── AdRail.tsx
│ │ │ ├── AdMobileBar.tsx
│ │ │ └── index.ts
│ │ ├── content/ # Content display components
│ │ │ ├── ArticleCard.tsx
│ │ │ ├── ArticleList.tsx
│ │ │ ├── ArticleContent.tsx
│ │ │ ├── GuideOverview.tsx
│ │ │ ├── StepTimeline.tsx
│ │ │ ├── DocumentChecklist.tsx
│ │ │ ├── UpdateCard.tsx
│ │ │ ├── UpdateFeed.tsx
│ │ │ ├── FAQList.tsx
│ │ │ ├── FAQItem.tsx
│ │ │ └── index.ts
│ │ ├── forms/ # Form components
│ │ │ ├── QuestionForm.tsx
│ │ │ ├── LoginForm.tsx
│ │ │ ├── RegisterForm.tsx
│ │ │ ├── FilterBar.tsx
│ │ │ ├── SearchInput.tsx
│ │ │ └── index.ts
│ │ ├── modals/ # Modal dialogs
│ │ │ ├── QuestionModal.tsx
│ │ │ ├── AdvertiseModal.tsx
│ │ │ ├── AdBookingModal.tsx
│ │ │ ├── LoginModal.tsx
│ │ │ ├── RegisterModal.tsx
│ │ │ ├── ConfirmModal.tsx
│ │ │ └── index.ts
│ │ ├── admin/ # Admin-specific components
│ │ │ ├── Sidebar.tsx
│ │ │ ├── StatsCard.tsx
│ │ │ ├── DataTable.tsx
│ │ │ ├── FeatureToggle.tsx
│ │ │ └── index.ts
│ │ └── common/ # Shared utilities
│ │ ├── EmptyState.tsx
│ │ ├── ErrorBoundary.tsx
│ │ ├── LoadingSpinner.tsx
│ │ ├── ProtectedRoute.tsx
│ │ └── index.ts
│ │
│ ├── stores/ # Zustand state stores
│ │ ├── navigation-store.ts
│ │ ├── auth-store.ts
│ │ ├── config-store.ts
│ │ ├── ads-store.ts
│ │ ├── modal-store.ts
│ │ ├── locale-store.ts
│ │ └── index.ts
│ │
│ ├── hooks/ # Custom React hooks
│ │ ├── useAuth.ts
│ │ ├── useFeatureFlag.ts
│ │ ├── useAdRotation.ts
│ │ ├── useIntersectionImpression.ts
│ │ ├── useMockVisitors.ts
│ │ ├── useLocale.ts
│ │ ├── useMediaQuery.ts
│ │ └── index.ts
│ │
│ ├── lib/ # Utili
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title: Administrative Aide Visualizer
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Administrative Aide Visualizer 🏛️
|
| 3 |
+
colorFrom: yellow
|
| 4 |
+
colorTo: red
|
| 5 |
+
emoji: 🐳
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
|
@@ -1,19 +1,253 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Administrative Aide Visualizer</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 9 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 10 |
+
<link rel="stylesheet" href="style.css">
|
| 11 |
+
<script>
|
| 12 |
+
tailwind.config = {
|
| 13 |
+
theme: {
|
| 14 |
+
extend: {
|
| 15 |
+
colors: {
|
| 16 |
+
primary: {
|
| 17 |
+
50: '#f0f9ff',
|
| 18 |
+
100: '#e0f2fe',
|
| 19 |
+
200: '#bae6fd',
|
| 20 |
+
300: '#7dd3fc',
|
| 21 |
+
400: '#38bdf8',
|
| 22 |
+
500: '#0ea5e9',
|
| 23 |
+
600: '#0284c7',
|
| 24 |
+
700: '#0369a1',
|
| 25 |
+
800: '#075985',
|
| 26 |
+
900: '#0c4a6e',
|
| 27 |
+
},
|
| 28 |
+
secondary: {
|
| 29 |
+
50: '#f5f3ff',
|
| 30 |
+
100: '#ede9fe',
|
| 31 |
+
200: '#ddd6fe',
|
| 32 |
+
300: '#c4b5fd',
|
| 33 |
+
400: '#a78bfa',
|
| 34 |
+
500: '#8b5cf6',
|
| 35 |
+
600: '#7c3aed',
|
| 36 |
+
700: '#6d28d9',
|
| 37 |
+
800: '#5b21b6',
|
| 38 |
+
900: '#4c1d95',
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
</script>
|
| 45 |
+
</head>
|
| 46 |
+
<body class="bg-gray-50">
|
| 47 |
+
<!-- Navigation -->
|
| 48 |
+
<nav class="bg-white shadow-sm">
|
| 49 |
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 50 |
+
<div class="flex justify-between h-16">
|
| 51 |
+
<div class="flex items-center">
|
| 52 |
+
<i data-feather="book" class="text-primary-600 mr-2"></i>
|
| 53 |
+
<span class="text-xl font-bold text-gray-800">AdminAide</span>
|
| 54 |
+
</div>
|
| 55 |
+
<div class="flex items-center space-x-4">
|
| 56 |
+
<a href="#" class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-primary-600">Home</a>
|
| 57 |
+
<a href="#" class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-primary-600">Guides</a>
|
| 58 |
+
<a href="#" class="px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-primary-600">FAQ</a>
|
| 59 |
+
<button class="bg-primary-500 text-white px-4 py-2 rounded-md text-sm font-medium hover:bg-primary-600">
|
| 60 |
+
Ask Question
|
| 61 |
+
</button>
|
| 62 |
+
</div>
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
</nav>
|
| 66 |
+
|
| 67 |
+
<!-- Hero Section -->
|
| 68 |
+
<div class="bg-gradient-to-r from-primary-500 to-secondary-500">
|
| 69 |
+
<div class="max-w-7xl mx-auto py-12 px-4 sm:px-6 lg:px-8 lg:py-24">
|
| 70 |
+
<div class="text-center">
|
| 71 |
+
<h1 class="text-4xl font-extrabold tracking-tight text-white sm:text-5xl lg:text-6xl">
|
| 72 |
+
Navigate French Administration
|
| 73 |
+
</h1>
|
| 74 |
+
<p class="mt-6 max-w-3xl mx-auto text-xl text-primary-100">
|
| 75 |
+
Comprehensive guides for students, employees, and citizenship applicants
|
| 76 |
+
</p>
|
| 77 |
+
<div class="mt-10 flex justify-center space-x-4">
|
| 78 |
+
<a href="#" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-primary-700 bg-white hover:bg-gray-50">
|
| 79 |
+
Get Started
|
| 80 |
+
</a>
|
| 81 |
+
<a href="#" class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-primary-700 bg-opacity-60 hover:bg-opacity-70">
|
| 82 |
+
Watch Video
|
| 83 |
+
</a>
|
| 84 |
+
</div>
|
| 85 |
+
</div>
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
|
| 89 |
+
<!-- Main Content -->
|
| 90 |
+
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
| 91 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
| 92 |
+
<!-- Student Card -->
|
| 93 |
+
<div class="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-105">
|
| 94 |
+
<div class="bg-primary-100 p-6">
|
| 95 |
+
<div class="flex items-center">
|
| 96 |
+
<i data-feather="book-open" class="text-primary-600 mr-3"></i>
|
| 97 |
+
<h2 class="text-xl font-bold text-gray-800">Students</h2>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
<div class="p-6">
|
| 101 |
+
<p class="text-gray-600 mb-4">Everything about student visas, university registration, and scholarship applications.</p>
|
| 102 |
+
<a href="#" class="inline-flex items-center text-primary-600 hover:text-primary-800">
|
| 103 |
+
Student guide
|
| 104 |
+
<i data-feather="arrow-right" class="ml-1 w-4 h-4"></i>
|
| 105 |
+
</a>
|
| 106 |
+
</div>
|
| 107 |
+
</div>
|
| 108 |
+
|
| 109 |
+
<!-- Employee Card -->
|
| 110 |
+
<div class="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-105">
|
| 111 |
+
<div class="bg-secondary-100 p-6">
|
| 112 |
+
<div class="flex items-center">
|
| 113 |
+
<i data-feather="briefcase" class="text-secondary-600 mr-3"></i>
|
| 114 |
+
<h2 class="text-xl font-bold text-gray-800">Employees</h2>
|
| 115 |
+
</div>
|
| 116 |
+
</div>
|
| 117 |
+
<div class="p-6">
|
| 118 |
+
<p class="text-gray-600 mb-4">Work permits, employment contracts, and professional visa requirements.</p>
|
| 119 |
+
<a href="#" class="inline-flex items-center text-secondary-600 hover:text-secondary-800">
|
| 120 |
+
Employee guide
|
| 121 |
+
<i data-feather="arrow-right" class="ml-1 w-4 h-4"></i>
|
| 122 |
+
</a>
|
| 123 |
+
</div>
|
| 124 |
+
</div>
|
| 125 |
+
|
| 126 |
+
<!-- Naturalization Card -->
|
| 127 |
+
<div class="bg-white rounded-lg shadow-md overflow-hidden transition-transform hover:scale-105">
|
| 128 |
+
<div class="bg-gray-100 p-6">
|
| 129 |
+
<div class="flex items-center">
|
| 130 |
+
<i data-feather="flag" class="text-gray-600 mr-3"></i>
|
| 131 |
+
<h2 class="text-xl font-bold text-gray-800">Naturalization</h2>
|
| 132 |
+
</div>
|
| 133 |
+
</div>
|
| 134 |
+
<div class="p-6">
|
| 135 |
+
<p class="text-gray-600 mb-4">Citizenship application process, eligibility criteria, and interview preparation.</p>
|
| 136 |
+
<a href="#" class="inline-flex items-center text-gray-600 hover:text-gray-800">
|
| 137 |
+
Citizenship guide
|
| 138 |
+
<i data-feather="arrow-right" class="ml-1 w-4 h-4"></i>
|
| 139 |
+
</a>
|
| 140 |
+
</div>
|
| 141 |
+
</div>
|
| 142 |
+
</div>
|
| 143 |
+
|
| 144 |
+
<!-- Updates Section -->
|
| 145 |
+
<div class="mt-16">
|
| 146 |
+
<h2 class="text-2xl font-bold text-gray-800 mb-6">Latest Updates</h2>
|
| 147 |
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
| 148 |
+
<!-- Update 1 -->
|
| 149 |
+
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
| 150 |
+
<div class="p-6">
|
| 151 |
+
<div class="flex items-center mb-3">
|
| 152 |
+
<span class="bg-primary-100 text-primary-800 text-xs font-semibold px-2.5 py-0.5 rounded">New</span>
|
| 153 |
+
<span class="text-gray-500 text-sm ml-2">May 15, 2023</span>
|
| 154 |
+
</div>
|
| 155 |
+
<h3 class="text-lg font-semibold text-gray-800 mb-2">Student Visa Fee Changes</h3>
|
| 156 |
+
<p class="text-gray-600 mb-4">The French government has announced updated fees for student visa applications effective June 1st.</p>
|
| 157 |
+
<a href="#" class="text-primary-600 hover:text-primary-800 text-sm font-medium">Read more →</a>
|
| 158 |
+
</div>
|
| 159 |
+
</div>
|
| 160 |
+
|
| 161 |
+
<!-- Update 2 -->
|
| 162 |
+
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
| 163 |
+
<div class="p-6">
|
| 164 |
+
<div class="flex items-center mb-3">
|
| 165 |
+
<span class="bg-secondary-100 text-secondary-800 text-xs font-semibold px-2.5 py-0.5 rounded">Update</span>
|
| 166 |
+
<span class="text-gray-500 text-sm ml-2">May 10, 2023</span>
|
| 167 |
+
</div>
|
| 168 |
+
<h3 class="text-lg font-semibold text-gray-800 mb-2">Work Permit Processing Times</h3>
|
| 169 |
+
<p class="text-gray-600 mb-4">Current processing times for employee work permits have been reduced by 15% in most regions.</p>
|
| 170 |
+
<a href="#" class="text-primary-600 hover:text-primary-800 text-sm font-medium">Read more →</a>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
|
| 174 |
+
<!-- Update 3 -->
|
| 175 |
+
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
| 176 |
+
<div class="p-6">
|
| 177 |
+
<div class="flex items-center mb-3">
|
| 178 |
+
<span class="bg-gray-100 text-gray-800 text-xs font-semibold px-2.5 py-0.5 rounded">Notice</span>
|
| 179 |
+
<span class="text-gray-500 text-sm ml-2">May 5, 2023</span>
|
| 180 |
+
</div>
|
| 181 |
+
<h3 class="text-lg font-semibold text-gray-800 mb-2">Citizenship Test Updates</h3>
|
| 182 |
+
<p class="text-gray-600 mb-4">The Ministry has published new study materials for the French language and civics tests.</p>
|
| 183 |
+
<a href="#" class="text-primary-600 hover:text-primary-800 text-sm font-medium">Read more →</a>
|
| 184 |
+
</div>
|
| 185 |
+
</div>
|
| 186 |
+
</div>
|
| 187 |
+
</div>
|
| 188 |
+
</main>
|
| 189 |
+
|
| 190 |
+
<!-- Footer -->
|
| 191 |
+
<footer class="bg-gray-800 text-white py-12">
|
| 192 |
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
| 193 |
+
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
|
| 194 |
+
<div>
|
| 195 |
+
<h3 class="text-lg font-semibold mb-4">Administrative Aide</h3>
|
| 196 |
+
<p class="text-gray-400">Simplifying French administrative processes for international visitors and residents.</p>
|
| 197 |
+
</div>
|
| 198 |
+
<div>
|
| 199 |
+
<h4 class="text-sm font-semibold uppercase tracking-wider mb-4">Guides</h4>
|
| 200 |
+
<ul class="space-y-2">
|
| 201 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Student Visa</a></li>
|
| 202 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Work Permits</a></li>
|
| 203 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Citizenship</a></li>
|
| 204 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Residency</a></li>
|
| 205 |
+
</ul>
|
| 206 |
+
</div>
|
| 207 |
+
<div>
|
| 208 |
+
<h4 class="text-sm font-semibold uppercase tracking-wider mb-4">Resources</h4>
|
| 209 |
+
<ul class="space-y-2">
|
| 210 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Forms & Documents</a></li>
|
| 211 |
+
<li><a href="#" class="text-gray-400 hover:text-white">FAQ</a></li>
|
| 212 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Glossary</a></li>
|
| 213 |
+
<li><a href="#" class="text-gray-400 hover:text-white">Contact Prefectures</a></li>
|
| 214 |
+
</ul>
|
| 215 |
+
</div>
|
| 216 |
+
<div>
|
| 217 |
+
<h4 class="text-sm font-semibold uppercase tracking-wider mb-4">Connect</h4>
|
| 218 |
+
<div class="flex space-x-4">
|
| 219 |
+
<a href="#" class="text-gray-400 hover:text-white">
|
| 220 |
+
<i data-feather="facebook"></i>
|
| 221 |
+
</a>
|
| 222 |
+
<a href="#" class="text-gray-400 hover:text-white">
|
| 223 |
+
<i data-feather="twitter"></i>
|
| 224 |
+
</a>
|
| 225 |
+
<a href="#" class="text-gray-400 hover:text-white">
|
| 226 |
+
<i data-feather="linkedin"></i>
|
| 227 |
+
</a>
|
| 228 |
+
<a href="#" class="text-gray-400 hover:text-white">
|
| 229 |
+
<i data-feather="mail"></i>
|
| 230 |
+
</a>
|
| 231 |
+
</div>
|
| 232 |
+
<div class="mt-6">
|
| 233 |
+
<button class="flex items-center text-gray-400 hover:text-white text-sm">
|
| 234 |
+
<i data-feather="globe" class="mr-2"></i>
|
| 235 |
+
English
|
| 236 |
+
<i data-feather="chevron-down" class="ml-1"></i>
|
| 237 |
+
</button>
|
| 238 |
+
</div>
|
| 239 |
+
</div>
|
| 240 |
+
</div>
|
| 241 |
+
<div class="mt-12 pt-8 border-t border-gray-700">
|
| 242 |
+
<p class="text-gray-400 text-sm text-center">© 2023 Administrative Aide Visualizer. All rights reserved.</p>
|
| 243 |
+
</div>
|
| 244 |
+
</div>
|
| 245 |
+
</footer>
|
| 246 |
+
|
| 247 |
+
<script src="script.js"></script>
|
| 248 |
+
<script>
|
| 249 |
+
feather.replace();
|
| 250 |
+
</script>
|
| 251 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 252 |
+
</body>
|
| 253 |
+
</html>
|
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
document.addEventListener('DOMContentLoaded', function() {
|
| 2 |
+
// Initialize tooltips
|
| 3 |
+
const tooltipTriggers = document.querySelectorAll('[data-tooltip]');
|
| 4 |
+
tooltipTriggers.forEach(trigger => {
|
| 5 |
+
const tooltip = document.createElement('div');
|
| 6 |
+
tooltip.className = 'hidden bg-gray-800 text-white text-xs rounded py-1 px-2 absolute z-50';
|
| 7 |
+
tooltip.textContent = trigger.getAttribute('data-tooltip');
|
| 8 |
+
document.body.appendChild(tooltip);
|
| 9 |
+
|
| 10 |
+
trigger.addEventListener('mouseenter', () => {
|
| 11 |
+
const rect = trigger.getBoundingClientRect();
|
| 12 |
+
tooltip.style.top = `${rect.top - 30}px`;
|
| 13 |
+
tooltip.style.left = `${rect.left + rect.width / 2}px`;
|
| 14 |
+
tooltip.style.transform = 'translateX(-50%)';
|
| 15 |
+
tooltip.classList.remove('hidden');
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
+
trigger.addEventListener('mouseleave', () => {
|
| 19 |
+
tooltip.classList.add('hidden');
|
| 20 |
+
});
|
| 21 |
+
});
|
| 22 |
+
|
| 23 |
+
// Mock visitor counter animation
|
| 24 |
+
const visitorCounter = document.getElementById('visitor-counter');
|
| 25 |
+
if (visitorCounter) {
|
| 26 |
+
let count = Math.floor(Math.random() * 100) + 50;
|
| 27 |
+
setInterval(() => {
|
| 28 |
+
count += Math.floor(Math.random() * 3) - 1;
|
| 29 |
+
visitorCounter.textContent = count.toLocaleString() + ' Active Visitors';
|
| 30 |
+
}, 5000);
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
// Form submission handling
|
| 34 |
+
const forms = document.querySelectorAll('form');
|
| 35 |
+
forms.forEach(form => {
|
| 36 |
+
form.addEventListener('submit', function(e) {
|
| 37 |
+
e.preventDefault();
|
| 38 |
+
const submitButton = form.querySelector('button[type="submit"]');
|
| 39 |
+
if (submitButton) {
|
| 40 |
+
const originalText = submitButton.textContent;
|
| 41 |
+
submitButton.innerHTML = '<i data-feather="loader" class="animate-spin mr-2 w-4 h-4"></i> Processing...';
|
| 42 |
+
feather.replace();
|
| 43 |
+
|
| 44 |
+
// Simulate API call
|
| 45 |
+
setTimeout(() => {
|
| 46 |
+
submitButton.textContent = originalText;
|
| 47 |
+
// Show success message
|
| 48 |
+
alert('Form submitted successfully!');
|
| 49 |
+
}, 1500);
|
| 50 |
+
}
|
| 51 |
+
});
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
// Mobile menu toggle (if exists)
|
| 55 |
+
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
| 56 |
+
const mobileMenu = document.getElementById('mobile-menu');
|
| 57 |
+
if (mobileMenuButton && mobileMenu) {
|
| 58 |
+
mobileMenuButton.addEventListener('click', () => {
|
| 59 |
+
mobileMenu.classList.toggle('hidden');
|
| 60 |
+
});
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
// Smooth scrolling for anchor links
|
| 64 |
+
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
| 65 |
+
anchor.addEventListener('click', function(e) {
|
| 66 |
+
e.preventDefault();
|
| 67 |
+
document.querySelector(this.getAttribute('href')).scrollIntoView({
|
| 68 |
+
behavior: 'smooth'
|
| 69 |
+
});
|
| 70 |
+
});
|
| 71 |
+
});
|
| 72 |
+
|
| 73 |
+
// Initialize all feather icons
|
| 74 |
+
feather.replace();
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
// Debounce function for resize/scroll events
|
| 78 |
+
function debounce(func, wait = 20, immediate = true) {
|
| 79 |
+
let timeout;
|
| 80 |
+
return function() {
|
| 81 |
+
const context = this, args = arguments;
|
| 82 |
+
const later = function() {
|
| 83 |
+
timeout = null;
|
| 84 |
+
if (!immediate) func.apply(context, args);
|
| 85 |
+
};
|
| 86 |
+
const callNow = immediate && !timeout;
|
| 87 |
+
clearTimeout(timeout);
|
| 88 |
+
timeout = setTimeout(later, wait);
|
| 89 |
+
if (callNow) func.apply(context, args);
|
| 90 |
+
};
|
| 91 |
+
}
|
|
@@ -1,28 +1,56 @@
|
|
|
|
|
|
|
|
| 1 |
body {
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
margin-top: 5px;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 2 |
+
|
| 3 |
body {
|
| 4 |
+
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
| 5 |
+
-webkit-font-smoothing: antialiased;
|
| 6 |
+
line-height: 1.6;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
/* Custom animations */
|
| 10 |
+
@keyframes fadeIn {
|
| 11 |
+
from { opacity: 0; transform: translateY(10px); }
|
| 12 |
+
to { opacity: 1; transform: translateY(0); }
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.animate-fade-in {
|
| 16 |
+
animation: fadeIn 0.5s ease-out forwards;
|
| 17 |
}
|
| 18 |
|
| 19 |
+
/* Custom card hover effect */
|
| 20 |
+
.card-hover:hover {
|
| 21 |
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
| 22 |
+
transform: translateY(-2px);
|
| 23 |
+
transition: all 0.3s ease;
|
| 24 |
}
|
| 25 |
|
| 26 |
+
/* Custom button focus state */
|
| 27 |
+
.button-focus:focus {
|
| 28 |
+
outline: none;
|
| 29 |
+
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.5);
|
|
|
|
| 30 |
}
|
| 31 |
|
| 32 |
+
/* Responsive typography */
|
| 33 |
+
@media (min-width: 1024px) {
|
| 34 |
+
.text-responsive {
|
| 35 |
+
font-size: 1.125rem;
|
| 36 |
+
}
|
|
|
|
| 37 |
}
|
| 38 |
|
| 39 |
+
/* Custom scrollbar */
|
| 40 |
+
::-webkit-scrollbar {
|
| 41 |
+
width: 8px;
|
| 42 |
+
height: 8px;
|
| 43 |
}
|
| 44 |
+
|
| 45 |
+
::-webkit-scrollbar-track {
|
| 46 |
+
background: #f1f1f1;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
::-webkit-scrollbar-thumb {
|
| 50 |
+
background: #888;
|
| 51 |
+
border-radius: 4px;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
::-webkit-scrollbar-thumb:hover {
|
| 55 |
+
background: #555;
|
| 56 |
+
}
|