diff --git a/GEMINI.md b/GEMINI.md index 53fff26..a60772f 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -1,24 +1,82 @@ -# Intranet of schools +# Intranet of Schools (INS) Project Context -The intranet of schools is a flutter project worked on by the 'Record Breakers'. -It is a flutter application made to work on android, linux and the web. It communicates -with a web backend via hooks exported from lib/backend.dart. +This document provides essential context for the "Intranet of Schools" Flutter project to aid in development and maintenance. -## project structure +## 1. Project Overview -source code in lib/, arb translations at lib/l19n, backend models at lib/models/ . utility -widgets at lib/widgets/, utilities at lib/utils, app at lib/app.dart, theme at lib/theme.dart. +- **Name:** `ins` (Intranet of Schools) +- **Description:** A responsive Flutter application providing a user interface for the Intranet of Schools platform. It's designed for managing user accounts, interacting with platform features, and accessing school-related information across mobile, web, and desktop. +- **Target Platforms:** Android, iOS, Linux, macOS, Windows, and Web. -## startup +## 2. Project Structure -When the app startups, the app state is loaded using utilities at lib/appstate.dart -Where is stored the session, user, and other date. +The project follows a standard Flutter structure. -When a user has signin or signup using the two assistant pages folders at lib/sign/signup/ and lib/sign/signin/ -a session and/or user is created and saved in appstate. -If the user is login, depending if he integrated school or not and the role in the school, -it opens the set of pages at lib/pages/dashboard/*/. +- `lib/`: Main application source code. + - `main.dart`: Application entry point. + - `app.dart`: Root application widget and setup. + - `appstate.dart`: Manages global application state (session, user data) likely using `provider`. + - `backend.dart`: Contains hooks and functions for communicating with the backend API. + - `models/`: Data models for entities like `User`, `School`, `Session`, `ChatMessage`, etc. + - `pages/`: UI screens for different parts of the app (e.g., `home`, `signin`, `dashboard`). + - `widgets/`: Reusable UI components (e.g., `locale_chooser`, `loading`). + - `utils/`: Utility functions (e.g., `logger`, `email_validator`). + - `theme.dart`: Application-wide theme and styling. + - `l10n/`: Localization files. +- `assets/`: Static assets. + - `assets/icon/`: Application icons. +- `test/`: Widget and unit tests. +- Platform-specific directories: `android/`, `ios/`, `linux/`, `macos/`, `windows/`, `web/`. -## assets +## 3. Dependencies -app assets are at assets, icons at assets/icons/ and the app icon at assets/icons/is.png +Key dependencies from `pubspec.yaml`: + +- **State Management:** + - `provider`: For managing application state. +- **UI & UX:** + - `google_fonts`: For custom fonts. + - `cupertino_icons`: iOS style icons. + - `flutter_markdown`: To render Markdown content. + - `cached_network_image`: To display and cache network images. + - `transparent_image`: For placeholder images. + - `intl_phone_number_input`: For formatted phone number input. +- **Networking & Backend:** + - `http`: For making HTTP requests to the backend. +- **Storage:** + - `shared_preferences`: For simple key-value storage. +- **Utilities:** + - `url_launcher`: To launch URLs. + - `logger`: For application logging. +- **Localization:** + - `flutter_localizations` & `intl`: For internationalization and localization. + +## 4. Code Style and Linting + +- The project uses the standard `package:flutter_lints/flutter.yaml` ruleset. +- Custom linting rules can be configured in `analysis_options.yaml`. + +## 5. Localization (l10n) + +- **Configuration:** `l10n.yaml` +- **Source Directory:** `lib/l10n` +- **Template File:** `app_en.arb` (English) +- **Process:** The app supports internationalization. New translations should be added as `.arb` files in `lib/l10n/` and code generation must be run to update localizations. + +## 6. Build & Deployment + +- **Firebase Hosting:** The project is configured to deploy the web build to Firebase Hosting. + - The public directory is `build/web`. + - Configuration is in `firebase.json`. +- **CI/CD:** A GitHub Actions workflow is set up in `.github/workflows/firebase-hosting-pull-request.yml`. + - **Trigger:** On pull requests to the main repository. + - **Job:** It builds the Flutter web application (`flutter build web`) and deploys it to a Firebase Hosting preview channel. + - **Secrets:** Requires `FIREBASE_SERVICE_ACCOUNT_INTRANET_OF_SCHOOLS` for deployment. + +## 7. Application Startup Flow + +1. The app starts via `main.dart`. +2. The global `AppState` is loaded from `appstate.dart`, which likely restores session/user data from `shared_preferences`. +3. If a user is not logged in, they are directed to the sign-in/sign-up pages (`lib/pages/sign/`). +4. Upon successful login, a session and user object are created and stored in `AppState`. +5. The user is then navigated to the appropriate dashboard (`lib/pages/dashboard/`) based on their role and school association. \ No newline at end of file diff --git a/android/.kotlin/sessions/kotlin-compiler-5112185348243535662.salive b/android/.kotlin/sessions/kotlin-compiler-5112185348243535662.salive new file mode 100644 index 0000000..e69de29 diff --git a/android/.kotlin/sessions/kotlin-compiler-8168454384994847072.salive b/android/.kotlin/sessions/kotlin-compiler-8168454384994847072.salive new file mode 100644 index 0000000..e69de29 diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index d9e8cf3..c1af55c 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -24,10 +24,11 @@ android { applicationId = "cm.rbs.ins" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. - minSdk = flutter.minSdkVersion + minSdk = 21 targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName + multiDexEnabled = true } buildTypes { diff --git a/android/app/src/main/kotlin/com/example/ins/MainActivity.kt b/android/app/src/main/kotlin/cm/rbs/ins/MainActivity.kt similarity index 79% rename from android/app/src/main/kotlin/com/example/ins/MainActivity.kt rename to android/app/src/main/kotlin/cm/rbs/ins/MainActivity.kt index 833d6b7..5d7936c 100644 --- a/android/app/src/main/kotlin/com/example/ins/MainActivity.kt +++ b/android/app/src/main/kotlin/cm/rbs/ins/MainActivity.kt @@ -1,4 +1,4 @@ -package com.example.ins +package cm.rbs.ins import io.flutter.embedding.android.FlutterActivity diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index ac377ac..cfbc5d6 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,36 +1,72 @@ { + "acceptContinue": "Accept & Continue", + "contactInformation": "Contact Information", + "accountCreatedSuccesfuly": "#Account succefsully created", + "invalidDomainComponent": "Invalid domain component", + "loadingTerms": "Loading terms of service", + "passwordsDoNotMatch": "Passwords do not match", + "noSchoolFound": "No school found", + "nothingHere": "Nothing here", + "errorLoadingYourDashboard": "Error loading your dashboard", + "fullName": "Full name", + "welcomeToIS": "Welcome to IS", + "emailShouldContain1": "Email should contain 1 '@'", + "whatDoYouWantToApplyFor": "What do you want to apply for?", + "wasNotTranslated": "Was not translated", + "students": "Students", "sloganShort": "Empowering connections, empowering futures", - "emailAddress": "Email address", - "optionalInformations": "Optional information", - "reenterPassword": "Reenter password", - "accountCreatedSuccesfuly": "Account created succesfuly", + "optionalInformations": "#Informations optionnelles", "waitingMessages": "We're getting this done...|Please wait a little more...|Things are going as expected...|...|Wait once more...|A few moments...", - "pleaseReviewAndAcceptOurTermsAndConditionsToContinue": "Please review and accept our terms and conditions to continue.", - "signupAssistant_phoneNumberError": "Phone number should be 9 digits long (without country code)", - "loadingTerms": "Loading terms of service", - "homeLoadingMessages": "Loading app state...|Verifying user details...|Getting permissions from your schools..|Verifying the age on your birth certificate...|Please wait...|Almost there...", - "passwordsDoNotMatch": "Password do not match", + "invalidEmailDomainsecondPart": "#Domaine de l'e-mail invalide (deuxième partie)", + "couldNotGetSchooolApplicationForms": "#Nous n'avons pas pu recevoid les formulaires d'inscription", + "passwordShouldHaveAtLeast8Characters": "Password should have at least 8 characters length", + "rating": "Rating", "openDashboard": "Open dashboard", - "username": "Username", - "addEmailaoPhoneNumber": "Add email and/or phone number", - "loginInformations": "Login information", + "editPreferences": "Edit Preferences", + "addEmailaoPhoneNumber": "#Renseigner une adresse e-mail et/ou un numéro de téléphone", + "campus": "Campus", + "applyNow": "Apply Now", "usernameDesc": "A short public name visible by others", - "usernameMustHaveBetween4And20Characters": "Username must have between 3 and 20 characters", + "usernameMustHaveBetween4And20Characters": "###Le nom d'utilisateur doit contenir entre 3 et 20 caractères", "helloWorldOfThings": "hello world of things", "password": "Password", - "thisUsernameIsAlreadyTaken": "This username is already taken", - "fullName": "Full name", - "creatingYourAccount": "Creating you account", - "welcomeToIS": "Welcome to IS", - "welcomeConnectOrCreateAccount": "Connect an existing account or create a new one to embark on your journey with us", "connectAccount": "Connect account", + "usernameMustHaveBetween3And20Characters": "Username must have between 3 and 20 characters", + "emailShouldContain": "Email should contain '@'", + "continueGt": "Continue >", + "accountCreatedSuccessfully": "Account created successfully", + "emailAddress": "Email address", + "optionalInformation": "Optional information", + "pleaseReviewAndAcceptOurTermsAndConditionsToContinue": "Please review and accept our terms and conditions to continue.", + "signupAssistant_phoneNumberError": "Phone number should be 9 digits long (without country code)", + "online": "Online", + "homeLoadingMessages": "Loading app state...|Verifying user details...|Getting permissions from your schools..|Verifying the age on your birth certificate...|Please wait...|Almost there...", + "atAGlance": "At a Glance", + "browseSchools": "Browse Schools", + "thisUsernameIsAlreadyTaken": "This username is already taken", + "creatingYourAccount": "Creating your account", + "emailNameTooShort": "Email name too short", + "loadingYourDashboard": "Loading your dashboard", + "couldNotGetSchoolApplicationForms": "Could not get school application forms", "welcomeExcl": "Welcome!", + "schoolApplication": "School Application", + "noDescriptionProvided": "No description provided.", + "errorLoadingTerms": "Error loading terms of service", + "areWeGoing": "Are we going?", + "editProfile": "Edit Profile", + "reenterPassword": "Re-enter password", + "username": "Username", + "loginInformations": "Login information", + "invalidCharacterInEmail": "Invalid character in email ", + "exploreSchools": "Explore Schools", + "invalidEmailDomainSecondPart": "Invalid email domain (second part)", + "iHaveReadAndAgreeToTheTermsAndConditions": "I have read and agree to the terms and conditions.", + "welcomeConnectOrCreateAccount": "Connect an existing account or create a new one to embark on your journey with us", "phoneNumber": "Phone number", "termsAndConditions": "Terms and Conditions", - "continueGt": "Continue >", - "passwordShouldHaveAtleast8Characters": "Password should have atleast 8 characters length", + "addEmailOrPhoneNumber": "Add email or phone number", + "establishedIn2023": "Established in 2023", + "passwordShouldHaveAtleast8Characters": "#Le mot de passe doit contenir au moins 8 caractères", "createAccount": "Create an account", - "errorLoadingTerms": "Error loading terms of service", - "wasNotTranslated": "Was not translated", "retry": "Retry" } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 7f4f638..5f6c0a7 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1,36 +1,72 @@ { - "sloganShort": "Construire des liens puissants, bâtir des avenirs prometteurs", - "emailAddress": "Addresse email", - "optionalInformations": "Informations optionelles", - "reenterPassword": "Re-entrez votre mot de passe", - "accountCreatedSuccesfuly": "Compte cree avec succes", - "waitingMessages": "Nous ourons bientot fini...|Attendez encore un peu...|Tout ce passe bien...|...|Un peu plus de temps...|Quelques instants d'attente...", - "pleaseReviewAndAcceptOurTermsAndConditionsToContinue": "#Please review and accept our terms and conditions to continue.", - "signupAssistant_phoneNumberError": "Le numero devrais faire 9 chiffres de long (Sans code iso)", - "loadingTerms": "#Loading terms of service", - "homeLoadingMessages": "Verification des details d'utilisateur...|Acquisition de permission de votre ecole..|Veuillez patienter...|Nous y sommes presque...", - "passwordsDoNotMatch": "Les mot-de-passe ne coincident pas", - "openDashboard": "Ouvrir le tableaux de bord", - "username": "Nom d'utilisateur", - "addEmailaoPhoneNumber": "Renseigner une addresse email et/ou numero de telephone", - "loginInformations": "Informations de connection", - "usernameDesc": "Un surnom court, et visible des autres", - "usernameMustHaveBetween4And20Characters": "Le nom d'utilisateur doit faire entre 3 et 20 characteres de long", - "helloWorldOfThings": "hello world of things", - "password": "Mot de passe", - "thisUsernameIsAlreadyTaken": "Ce nom d'utilisateur est deja prit", + "acceptContinue": "Accepter & Continuer", + "contactInformation": "Informations de contact", + "accountCreatedSuccesfuly": "##Account succefsully created", + "invalidDomainComponent": "Composant de domaine invalide", + "loadingTerms": "Chargement des conditions d'utilisation", + "passwordsDoNotMatch": "Les mots de passe ne correspondent pas", + "noSchoolFound": "Aucune école trouvée", + "nothingHere": "Rien ici", + "errorLoadingYourDashboard": "Erreur lors du chargement de votre tableau de bord", "fullName": "Nom complet", - "creatingYourAccount": "Nous creons votre compte", "welcomeToIS": "Bienvenue sur IS", - "welcomeConnectOrCreateAccount": "Connectez un compte existant ou creez en un nouveau pour utiliser IS", - "connectAccount": "Connectez votre compte", - "welcomeExcl": "Bienvenue!", - "phoneNumber": "Numero de telephone", - "termsAndConditions": "#Terms and Conditions", + "emailShouldContain1": "L'e-mail doit contenir un '@'", + "whatDoYouWantToApplyFor": "Pour quoi voulez-vous postuler ?", + "wasNotTranslated": "N'a pas été traduit", + "students": "Étudiants", + "sloganShort": "Des liens forts, un avenir prometteur", + "optionalInformations": "##Informations optionnelles", + "waitingMessages": "Nous aurons bientôt fini...|Attendez encore un peu...|Tout se passe bien...|...|Un peu plus de temps...|Quelques instants d'attente...", + "invalidEmailDomainsecondPart": "##Domaine de l'e-mail invalide (deuxième partie)", + "couldNotGetSchooolApplicationForms": "##Nous n'avons pas pu recevoid les formulaires d'inscription", + "passwordShouldHaveAtLeast8Characters": "Le mot de passe doit contenir au moins 8 caractères", + "rating": "Évaluation", + "openDashboard": "Ouvrir le tableau de bord", + "editPreferences": "Modifier les préférences", + "addEmailaoPhoneNumber": "##Renseigner une adresse e-mail et/ou un numéro de téléphone", + "campus": "Campus", + "applyNow": "Postuler maintenant", + "usernameDesc": "Un surnom court, et visible des autres", + "usernameMustHaveBetween4And20Characters": "####Le nom d'utilisateur doit contenir entre 3 et 20 caractères", + "helloWorldOfThings": "Salut le monde des choses", + "password": "Mot de passe", + "connectAccount": "Connecter votre compte", + "usernameMustHaveBetween3And20Characters": "Le nom d'utilisateur doit contenir entre 3 et 20 caractères", + "emailShouldContain": "L'e-mail doit contenir '@'", "continueGt": "Continuer >", - "passwordShouldHaveAtleast8Characters": "Le mot-de-passe doit faire au moins 8 characteres de long", - "createAccount": "Creez un compte", - "errorLoadingTerms": "#Error loading terms of service", - "wasNotTranslated": "#Was not translated", - "retry": "Reessayer" + "accountCreatedSuccessfully": "Compte créé avec succès", + "emailAddress": "Adresse e-mail", + "optionalInformation": "Informations optionnelles", + "pleaseReviewAndAcceptOurTermsAndConditionsToContinue": "Veuillez consulter et accepter nos conditions d'utilisation pour continuer.", + "signupAssistant_phoneNumberError": "Le numéro de téléphone doit comporter 9 chiffres (sans l'indicatif du pays)", + "online": "En ligne", + "homeLoadingMessages": "Chargement de l'état de l'application...|Vérification des détails de l'utilisateur...|Obtention des autorisations de vos écoles...|Vérification de l'âge sur votre acte de naissance...|Veuillez patienter...|Presque là...", + "atAGlance": "En un coup d'œil", + "browseSchools": "Parcourir les écoles", + "thisUsernameIsAlreadyTaken": "Ce nom d'utilisateur est déjà pris", + "creatingYourAccount": "Création de votre compte", + "emailNameTooShort": "Nom d'e-mail trop court", + "loadingYourDashboard": "Chargement de votre tableau de bord", + "couldNotGetSchoolApplicationForms": "Impossible de récupérer les formulaires de demande d'école", + "welcomeExcl": "Bienvenue !", + "schoolApplication": "Candidature scolaire", + "noDescriptionProvided": "Aucune description fournie.", + "errorLoadingTerms": "Erreur de chargement des conditions d'utilisation", + "areWeGoing": "On y va ?", + "editProfile": "Modifier le profil", + "reenterPassword": "Retapez votre mot de passe", + "username": "Nom d'utilisateur", + "loginInformations": "Informations de connexion", + "invalidCharacterInEmail": "Caractère invalide dans l'e-mail", + "exploreSchools": "Explorer les écoles", + "invalidEmailDomainSecondPart": "Domaine de l'e-mail invalide (deuxième partie)", + "iHaveReadAndAgreeToTheTermsAndConditions": "J'ai lu et j'accepte les conditions d'utilisation.", + "welcomeConnectOrCreateAccount": "Connectez un compte existant ou créez-en un nouveau pour commencer votre voyage avec nous", + "phoneNumber": "Numéro de téléphone", + "termsAndConditions": "Conditions d'utilisation", + "addEmailOrPhoneNumber": "Ajouter une adresse e-mail ou un numéro de téléphone", + "establishedIn2023": "Établi en 2023", + "passwordShouldHaveAtleast8Characters": "##Le mot de passe doit contenir au moins 8 caractères", + "createAccount": "Créer un compte", + "retry": "Réessayer" } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index a720699..d396719 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -98,71 +98,137 @@ abstract class AppLocalizations { Locale('fr'), ]; - /// No description provided for @sloganShort. + /// No description provided for @acceptContinue. /// /// In en, this message translates to: - /// **'Empowering connections, empowering futures'** - String get sloganShort; + /// **'Accept & Continue'** + String get acceptContinue; - /// No description provided for @emailAddress. + /// No description provided for @contactInformation. /// /// In en, this message translates to: - /// **'Email address'** - String get emailAddress; + /// **'Contact Information'** + String get contactInformation; - /// No description provided for @optionalInformations. + /// No description provided for @accountCreatedSuccesfuly. /// /// In en, this message translates to: - /// **'Optional information'** - String get optionalInformations; + /// **'#Account succefsully created'** + String get accountCreatedSuccesfuly; - /// No description provided for @reenterPassword. + /// No description provided for @invalidDomainComponent. /// /// In en, this message translates to: - /// **'Reenter password'** - String get reenterPassword; + /// **'Invalid domain component'** + String get invalidDomainComponent; - /// No description provided for @accountCreatedSuccesfuly. + /// No description provided for @loadingTerms. /// /// In en, this message translates to: - /// **'Account created succesfuly'** - String get accountCreatedSuccesfuly; + /// **'Loading terms of service'** + String get loadingTerms; - /// No description provided for @waitingMessages. + /// No description provided for @passwordsDoNotMatch. /// /// In en, this message translates to: - /// **'We\'re getting this done...|Please wait a little more...|Things are going as expected...|...|Wait once more...|A few moments...'** - String get waitingMessages; + /// **'Passwords do not match'** + String get passwordsDoNotMatch; - /// No description provided for @pleaseReviewAndAcceptOurTermsAndConditionsToContinue. + /// No description provided for @noSchoolFound. /// /// In en, this message translates to: - /// **'Please review and accept our terms and conditions to continue.'** - String get pleaseReviewAndAcceptOurTermsAndConditionsToContinue; + /// **'No school found'** + String get noSchoolFound; - /// No description provided for @signupAssistant_phoneNumberError. + /// No description provided for @nothingHere. /// /// In en, this message translates to: - /// **'Phone number should be 9 digits long (without country code)'** - String get signupAssistant_phoneNumberError; + /// **'Nothing here'** + String get nothingHere; - /// No description provided for @loadingTerms. + /// No description provided for @errorLoadingYourDashboard. /// /// In en, this message translates to: - /// **'Loading terms of service'** - String get loadingTerms; + /// **'Error loading your dashboard'** + String get errorLoadingYourDashboard; - /// No description provided for @homeLoadingMessages. + /// No description provided for @fullName. /// /// In en, this message translates to: - /// **'Loading app state...|Verifying user details...|Getting permissions from your schools..|Verifying the age on your birth certificate...|Please wait...|Almost there...'** - String get homeLoadingMessages; + /// **'Full name'** + String get fullName; - /// No description provided for @passwordsDoNotMatch. + /// No description provided for @welcomeToIS. /// /// In en, this message translates to: - /// **'Password do not match'** - String get passwordsDoNotMatch; + /// **'Welcome to IS'** + String get welcomeToIS; + + /// No description provided for @emailShouldContain1. + /// + /// In en, this message translates to: + /// **'Email should contain 1 \'@\''** + String get emailShouldContain1; + + /// No description provided for @whatDoYouWantToApplyFor. + /// + /// In en, this message translates to: + /// **'What do you want to apply for?'** + String get whatDoYouWantToApplyFor; + + /// No description provided for @wasNotTranslated. + /// + /// In en, this message translates to: + /// **'Was not translated'** + String get wasNotTranslated; + + /// No description provided for @students. + /// + /// In en, this message translates to: + /// **'Students'** + String get students; + + /// No description provided for @sloganShort. + /// + /// In en, this message translates to: + /// **'Empowering connections, empowering futures'** + String get sloganShort; + + /// No description provided for @optionalInformations. + /// + /// In en, this message translates to: + /// **'#Informations optionnelles'** + String get optionalInformations; + + /// No description provided for @waitingMessages. + /// + /// In en, this message translates to: + /// **'We\'re getting this done...|Please wait a little more...|Things are going as expected...|...|Wait once more...|A few moments...'** + String get waitingMessages; + + /// No description provided for @invalidEmailDomainsecondPart. + /// + /// In en, this message translates to: + /// **'#Domaine de l\'e-mail invalide (deuxième partie)'** + String get invalidEmailDomainsecondPart; + + /// No description provided for @couldNotGetSchooolApplicationForms. + /// + /// In en, this message translates to: + /// **'#Nous n\'avons pas pu recevoid les formulaires d\'inscription'** + String get couldNotGetSchooolApplicationForms; + + /// No description provided for @passwordShouldHaveAtLeast8Characters. + /// + /// In en, this message translates to: + /// **'Password should have at least 8 characters length'** + String get passwordShouldHaveAtLeast8Characters; + + /// No description provided for @rating. + /// + /// In en, this message translates to: + /// **'Rating'** + String get rating; /// No description provided for @openDashboard. /// @@ -170,23 +236,29 @@ abstract class AppLocalizations { /// **'Open dashboard'** String get openDashboard; - /// No description provided for @username. + /// No description provided for @editPreferences. /// /// In en, this message translates to: - /// **'Username'** - String get username; + /// **'Edit Preferences'** + String get editPreferences; /// No description provided for @addEmailaoPhoneNumber. /// /// In en, this message translates to: - /// **'Add email and/or phone number'** + /// **'#Renseigner une adresse e-mail et/ou un numéro de téléphone'** String get addEmailaoPhoneNumber; - /// No description provided for @loginInformations. + /// No description provided for @campus. /// /// In en, this message translates to: - /// **'Login information'** - String get loginInformations; + /// **'Campus'** + String get campus; + + /// No description provided for @applyNow. + /// + /// In en, this message translates to: + /// **'Apply Now'** + String get applyNow; /// No description provided for @usernameDesc. /// @@ -197,7 +269,7 @@ abstract class AppLocalizations { /// No description provided for @usernameMustHaveBetween4And20Characters. /// /// In en, this message translates to: - /// **'Username must have between 3 and 20 characters'** + /// **'###Le nom d\'utilisateur doit contenir entre 3 et 20 caractères'** String get usernameMustHaveBetween4And20Characters; /// No description provided for @helloWorldOfThings. @@ -212,41 +284,113 @@ abstract class AppLocalizations { /// **'Password'** String get password; - /// No description provided for @thisUsernameIsAlreadyTaken. + /// No description provided for @connectAccount. /// /// In en, this message translates to: - /// **'This username is already taken'** - String get thisUsernameIsAlreadyTaken; + /// **'Connect account'** + String get connectAccount; - /// No description provided for @fullName. + /// No description provided for @usernameMustHaveBetween3And20Characters. /// /// In en, this message translates to: - /// **'Full name'** - String get fullName; + /// **'Username must have between 3 and 20 characters'** + String get usernameMustHaveBetween3And20Characters; + + /// No description provided for @emailShouldContain. + /// + /// In en, this message translates to: + /// **'Email should contain \'@\''** + String get emailShouldContain; + + /// No description provided for @continueGt. + /// + /// In en, this message translates to: + /// **'Continue >'** + String get continueGt; + + /// No description provided for @accountCreatedSuccessfully. + /// + /// In en, this message translates to: + /// **'Account created successfully'** + String get accountCreatedSuccessfully; + + /// No description provided for @emailAddress. + /// + /// In en, this message translates to: + /// **'Email address'** + String get emailAddress; + + /// No description provided for @optionalInformation. + /// + /// In en, this message translates to: + /// **'Optional information'** + String get optionalInformation; + + /// No description provided for @pleaseReviewAndAcceptOurTermsAndConditionsToContinue. + /// + /// In en, this message translates to: + /// **'Please review and accept our terms and conditions to continue.'** + String get pleaseReviewAndAcceptOurTermsAndConditionsToContinue; + + /// No description provided for @signupAssistant_phoneNumberError. + /// + /// In en, this message translates to: + /// **'Phone number should be 9 digits long (without country code)'** + String get signupAssistant_phoneNumberError; + + /// No description provided for @online. + /// + /// In en, this message translates to: + /// **'Online'** + String get online; + + /// No description provided for @homeLoadingMessages. + /// + /// In en, this message translates to: + /// **'Loading app state...|Verifying user details...|Getting permissions from your schools..|Verifying the age on your birth certificate...|Please wait...|Almost there...'** + String get homeLoadingMessages; + + /// No description provided for @atAGlance. + /// + /// In en, this message translates to: + /// **'At a Glance'** + String get atAGlance; + + /// No description provided for @browseSchools. + /// + /// In en, this message translates to: + /// **'Browse Schools'** + String get browseSchools; + + /// No description provided for @thisUsernameIsAlreadyTaken. + /// + /// In en, this message translates to: + /// **'This username is already taken'** + String get thisUsernameIsAlreadyTaken; /// No description provided for @creatingYourAccount. /// /// In en, this message translates to: - /// **'Creating you account'** + /// **'Creating your account'** String get creatingYourAccount; - /// No description provided for @welcomeToIS. + /// No description provided for @emailNameTooShort. /// /// In en, this message translates to: - /// **'Welcome to IS'** - String get welcomeToIS; + /// **'Email name too short'** + String get emailNameTooShort; - /// No description provided for @welcomeConnectOrCreateAccount. + /// No description provided for @loadingYourDashboard. /// /// In en, this message translates to: - /// **'Connect an existing account or create a new one to embark on your journey with us'** - String get welcomeConnectOrCreateAccount; + /// **'Loading your dashboard'** + String get loadingYourDashboard; - /// No description provided for @connectAccount. + /// No description provided for @couldNotGetSchoolApplicationForms. /// /// In en, this message translates to: - /// **'Connect account'** - String get connectAccount; + /// **'Could not get school application forms'** + String get couldNotGetSchoolApplicationForms; /// No description provided for @welcomeExcl. /// @@ -254,6 +398,84 @@ abstract class AppLocalizations { /// **'Welcome!'** String get welcomeExcl; + /// No description provided for @schoolApplication. + /// + /// In en, this message translates to: + /// **'School Application'** + String get schoolApplication; + + /// No description provided for @noDescriptionProvided. + /// + /// In en, this message translates to: + /// **'No description provided.'** + String get noDescriptionProvided; + + /// No description provided for @errorLoadingTerms. + /// + /// In en, this message translates to: + /// **'Error loading terms of service'** + String get errorLoadingTerms; + + /// No description provided for @areWeGoing. + /// + /// In en, this message translates to: + /// **'Are we going?'** + String get areWeGoing; + + /// No description provided for @editProfile. + /// + /// In en, this message translates to: + /// **'Edit Profile'** + String get editProfile; + + /// No description provided for @reenterPassword. + /// + /// In en, this message translates to: + /// **'Re-enter password'** + String get reenterPassword; + + /// No description provided for @username. + /// + /// In en, this message translates to: + /// **'Username'** + String get username; + + /// No description provided for @loginInformations. + /// + /// In en, this message translates to: + /// **'Login information'** + String get loginInformations; + + /// No description provided for @invalidCharacterInEmail. + /// + /// In en, this message translates to: + /// **'Invalid character in email '** + String get invalidCharacterInEmail; + + /// No description provided for @exploreSchools. + /// + /// In en, this message translates to: + /// **'Explore Schools'** + String get exploreSchools; + + /// No description provided for @invalidEmailDomainSecondPart. + /// + /// In en, this message translates to: + /// **'Invalid email domain (second part)'** + String get invalidEmailDomainSecondPart; + + /// No description provided for @iHaveReadAndAgreeToTheTermsAndConditions. + /// + /// In en, this message translates to: + /// **'I have read and agree to the terms and conditions.'** + String get iHaveReadAndAgreeToTheTermsAndConditions; + + /// No description provided for @welcomeConnectOrCreateAccount. + /// + /// In en, this message translates to: + /// **'Connect an existing account or create a new one to embark on your journey with us'** + String get welcomeConnectOrCreateAccount; + /// No description provided for @phoneNumber. /// /// In en, this message translates to: @@ -266,16 +488,22 @@ abstract class AppLocalizations { /// **'Terms and Conditions'** String get termsAndConditions; - /// No description provided for @continueGt. + /// No description provided for @addEmailOrPhoneNumber. /// /// In en, this message translates to: - /// **'Continue >'** - String get continueGt; + /// **'Add email or phone number'** + String get addEmailOrPhoneNumber; + + /// No description provided for @establishedIn2023. + /// + /// In en, this message translates to: + /// **'Established in 2023'** + String get establishedIn2023; /// No description provided for @passwordShouldHaveAtleast8Characters. /// /// In en, this message translates to: - /// **'Password should have atleast 8 characters length'** + /// **'#Le mot de passe doit contenir au moins 8 caractères'** String get passwordShouldHaveAtleast8Characters; /// No description provided for @createAccount. @@ -284,18 +512,6 @@ abstract class AppLocalizations { /// **'Create an account'** String get createAccount; - /// No description provided for @errorLoadingTerms. - /// - /// In en, this message translates to: - /// **'Error loading terms of service'** - String get errorLoadingTerms; - - /// No description provided for @wasNotTranslated. - /// - /// In en, this message translates to: - /// **'Was not translated'** - String get wasNotTranslated; - /// No description provided for @retry. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 4da7529..73df91c 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -9,24 +9,126 @@ class AppLocalizationsEn extends AppLocalizations { AppLocalizationsEn([String locale = 'en']) : super(locale); @override - String get sloganShort => 'Empowering connections, empowering futures'; + String get acceptContinue => 'Accept & Continue'; @override - String get emailAddress => 'Email address'; + String get contactInformation => 'Contact Information'; + + @override + String get accountCreatedSuccesfuly => '#Account succefsully created'; + + @override + String get invalidDomainComponent => 'Invalid domain component'; + + @override + String get loadingTerms => 'Loading terms of service'; + + @override + String get passwordsDoNotMatch => 'Passwords do not match'; + + @override + String get noSchoolFound => 'No school found'; + + @override + String get nothingHere => 'Nothing here'; @override - String get optionalInformations => 'Optional information'; + String get errorLoadingYourDashboard => 'Error loading your dashboard'; @override - String get reenterPassword => 'Reenter password'; + String get fullName => 'Full name'; + + @override + String get welcomeToIS => 'Welcome to IS'; + + @override + String get emailShouldContain1 => 'Email should contain 1 \'@\''; + + @override + String get whatDoYouWantToApplyFor => 'What do you want to apply for?'; + + @override + String get wasNotTranslated => 'Was not translated'; + + @override + String get students => 'Students'; + + @override + String get sloganShort => 'Empowering connections, empowering futures'; @override - String get accountCreatedSuccesfuly => 'Account created succesfuly'; + String get optionalInformations => '#Informations optionnelles'; @override String get waitingMessages => 'We\'re getting this done...|Please wait a little more...|Things are going as expected...|...|Wait once more...|A few moments...'; + @override + String get invalidEmailDomainsecondPart => + '#Domaine de l\'e-mail invalide (deuxième partie)'; + + @override + String get couldNotGetSchooolApplicationForms => + '#Nous n\'avons pas pu recevoid les formulaires d\'inscription'; + + @override + String get passwordShouldHaveAtLeast8Characters => + 'Password should have at least 8 characters length'; + + @override + String get rating => 'Rating'; + + @override + String get openDashboard => 'Open dashboard'; + + @override + String get editPreferences => 'Edit Preferences'; + + @override + String get addEmailaoPhoneNumber => + '#Renseigner une adresse e-mail et/ou un numéro de téléphone'; + + @override + String get campus => 'Campus'; + + @override + String get applyNow => 'Apply Now'; + + @override + String get usernameDesc => 'A short public name visible by others'; + + @override + String get usernameMustHaveBetween4And20Characters => + '###Le nom d\'utilisateur doit contenir entre 3 et 20 caractères'; + + @override + String get helloWorldOfThings => 'hello world of things'; + + @override + String get password => 'Password'; + + @override + String get connectAccount => 'Connect account'; + + @override + String get usernameMustHaveBetween3And20Characters => + 'Username must have between 3 and 20 characters'; + + @override + String get emailShouldContain => 'Email should contain \'@\''; + + @override + String get continueGt => 'Continue >'; + + @override + String get accountCreatedSuccessfully => 'Account created successfully'; + + @override + String get emailAddress => 'Email address'; + + @override + String get optionalInformation => 'Optional information'; + @override String get pleaseReviewAndAcceptOurTermsAndConditionsToContinue => 'Please review and accept our terms and conditions to continue.'; @@ -36,83 +138,97 @@ class AppLocalizationsEn extends AppLocalizations { 'Phone number should be 9 digits long (without country code)'; @override - String get loadingTerms => 'Loading terms of service'; + String get online => 'Online'; @override String get homeLoadingMessages => 'Loading app state...|Verifying user details...|Getting permissions from your schools..|Verifying the age on your birth certificate...|Please wait...|Almost there...'; @override - String get passwordsDoNotMatch => 'Password do not match'; + String get atAGlance => 'At a Glance'; @override - String get openDashboard => 'Open dashboard'; + String get browseSchools => 'Browse Schools'; @override - String get username => 'Username'; + String get thisUsernameIsAlreadyTaken => 'This username is already taken'; @override - String get addEmailaoPhoneNumber => 'Add email and/or phone number'; + String get creatingYourAccount => 'Creating your account'; @override - String get loginInformations => 'Login information'; + String get emailNameTooShort => 'Email name too short'; @override - String get usernameDesc => 'A short public name visible by others'; + String get loadingYourDashboard => 'Loading your dashboard'; @override - String get usernameMustHaveBetween4And20Characters => - 'Username must have between 3 and 20 characters'; + String get couldNotGetSchoolApplicationForms => + 'Could not get school application forms'; @override - String get helloWorldOfThings => 'hello world of things'; + String get welcomeExcl => 'Welcome!'; @override - String get password => 'Password'; + String get schoolApplication => 'School Application'; @override - String get thisUsernameIsAlreadyTaken => 'This username is already taken'; + String get noDescriptionProvided => 'No description provided.'; @override - String get fullName => 'Full name'; + String get errorLoadingTerms => 'Error loading terms of service'; @override - String get creatingYourAccount => 'Creating you account'; + String get areWeGoing => 'Are we going?'; @override - String get welcomeToIS => 'Welcome to IS'; + String get editProfile => 'Edit Profile'; @override - String get welcomeConnectOrCreateAccount => - 'Connect an existing account or create a new one to embark on your journey with us'; + String get reenterPassword => 'Re-enter password'; @override - String get connectAccount => 'Connect account'; + String get username => 'Username'; @override - String get welcomeExcl => 'Welcome!'; + String get loginInformations => 'Login information'; @override - String get phoneNumber => 'Phone number'; + String get invalidCharacterInEmail => 'Invalid character in email '; @override - String get termsAndConditions => 'Terms and Conditions'; + String get exploreSchools => 'Explore Schools'; @override - String get continueGt => 'Continue >'; + String get invalidEmailDomainSecondPart => + 'Invalid email domain (second part)'; @override - String get passwordShouldHaveAtleast8Characters => - 'Password should have atleast 8 characters length'; + String get iHaveReadAndAgreeToTheTermsAndConditions => + 'I have read and agree to the terms and conditions.'; @override - String get createAccount => 'Create an account'; + String get welcomeConnectOrCreateAccount => + 'Connect an existing account or create a new one to embark on your journey with us'; @override - String get errorLoadingTerms => 'Error loading terms of service'; + String get phoneNumber => 'Phone number'; @override - String get wasNotTranslated => 'Was not translated'; + String get termsAndConditions => 'Terms and Conditions'; + + @override + String get addEmailOrPhoneNumber => 'Add email or phone number'; + + @override + String get establishedIn2023 => 'Established in 2023'; + + @override + String get passwordShouldHaveAtleast8Characters => + '#Le mot de passe doit contenir au moins 8 caractères'; + + @override + String get createAccount => 'Create an account'; @override String get retry => 'Retry'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index dbe78c4..ade6c4d 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -9,114 +9,231 @@ class AppLocalizationsFr extends AppLocalizations { AppLocalizationsFr([String locale = 'fr']) : super(locale); @override - String get sloganShort => - 'Construire des liens puissants, bâtir des avenirs prometteurs'; + String get acceptContinue => 'Accepter & Continuer'; @override - String get emailAddress => 'Addresse email'; + String get contactInformation => 'Informations de contact'; @override - String get optionalInformations => 'Informations optionelles'; + String get accountCreatedSuccesfuly => '##Account succefsully created'; @override - String get reenterPassword => 'Re-entrez votre mot de passe'; + String get invalidDomainComponent => 'Composant de domaine invalide'; @override - String get accountCreatedSuccesfuly => 'Compte cree avec succes'; + String get loadingTerms => 'Chargement des conditions d\'utilisation'; @override - String get waitingMessages => - 'Nous ourons bientot fini...|Attendez encore un peu...|Tout ce passe bien...|...|Un peu plus de temps...|Quelques instants d\'attente...'; + String get passwordsDoNotMatch => 'Les mots de passe ne correspondent pas'; @override - String get pleaseReviewAndAcceptOurTermsAndConditionsToContinue => - '#Please review and accept our terms and conditions to continue.'; + String get noSchoolFound => 'Aucune école trouvée'; @override - String get signupAssistant_phoneNumberError => - 'Le numero devrais faire 9 chiffres de long (Sans code iso)'; + String get nothingHere => 'Rien ici'; @override - String get loadingTerms => '#Loading terms of service'; + String get errorLoadingYourDashboard => + 'Erreur lors du chargement de votre tableau de bord'; @override - String get homeLoadingMessages => - 'Verification des details d\'utilisateur...|Acquisition de permission de votre ecole..|Veuillez patienter...|Nous y sommes presque...'; + String get fullName => 'Nom complet'; + + @override + String get welcomeToIS => 'Bienvenue sur IS'; @override - String get passwordsDoNotMatch => 'Les mot-de-passe ne coincident pas'; + String get emailShouldContain1 => 'L\'e-mail doit contenir un \'@\''; @override - String get openDashboard => 'Ouvrir le tableaux de bord'; + String get whatDoYouWantToApplyFor => 'Pour quoi voulez-vous postuler ?'; @override - String get username => 'Nom d\'utilisateur'; + String get wasNotTranslated => 'N\'a pas été traduit'; + + @override + String get students => 'Étudiants'; + + @override + String get sloganShort => 'Des liens forts, un avenir prometteur'; + + @override + String get optionalInformations => '##Informations optionnelles'; + + @override + String get waitingMessages => + 'Nous aurons bientôt fini...|Attendez encore un peu...|Tout se passe bien...|...|Un peu plus de temps...|Quelques instants d\'attente...'; + + @override + String get invalidEmailDomainsecondPart => + '##Domaine de l\'e-mail invalide (deuxième partie)'; + + @override + String get couldNotGetSchooolApplicationForms => + '##Nous n\'avons pas pu recevoid les formulaires d\'inscription'; + + @override + String get passwordShouldHaveAtLeast8Characters => + 'Le mot de passe doit contenir au moins 8 caractères'; + + @override + String get rating => 'Évaluation'; + + @override + String get openDashboard => 'Ouvrir le tableau de bord'; + + @override + String get editPreferences => 'Modifier les préférences'; @override String get addEmailaoPhoneNumber => - 'Renseigner une addresse email et/ou numero de telephone'; + '##Renseigner une adresse e-mail et/ou un numéro de téléphone'; + + @override + String get campus => 'Campus'; @override - String get loginInformations => 'Informations de connection'; + String get applyNow => 'Postuler maintenant'; @override String get usernameDesc => 'Un surnom court, et visible des autres'; @override String get usernameMustHaveBetween4And20Characters => - 'Le nom d\'utilisateur doit faire entre 3 et 20 characteres de long'; + '####Le nom d\'utilisateur doit contenir entre 3 et 20 caractères'; @override - String get helloWorldOfThings => 'hello world of things'; + String get helloWorldOfThings => 'Salut le monde des choses'; @override String get password => 'Mot de passe'; + @override + String get connectAccount => 'Connecter votre compte'; + + @override + String get usernameMustHaveBetween3And20Characters => + 'Le nom d\'utilisateur doit contenir entre 3 et 20 caractères'; + + @override + String get emailShouldContain => 'L\'e-mail doit contenir \'@\''; + + @override + String get continueGt => 'Continuer >'; + + @override + String get accountCreatedSuccessfully => 'Compte créé avec succès'; + + @override + String get emailAddress => 'Adresse e-mail'; + + @override + String get optionalInformation => 'Informations optionnelles'; + + @override + String get pleaseReviewAndAcceptOurTermsAndConditionsToContinue => + 'Veuillez consulter et accepter nos conditions d\'utilisation pour continuer.'; + + @override + String get signupAssistant_phoneNumberError => + 'Le numéro de téléphone doit comporter 9 chiffres (sans l\'indicatif du pays)'; + + @override + String get online => 'En ligne'; + + @override + String get homeLoadingMessages => + 'Chargement de l\'état de l\'application...|Vérification des détails de l\'utilisateur...|Obtention des autorisations de vos écoles...|Vérification de l\'âge sur votre acte de naissance...|Veuillez patienter...|Presque là...'; + + @override + String get atAGlance => 'En un coup d\'œil'; + + @override + String get browseSchools => 'Parcourir les écoles'; + @override String get thisUsernameIsAlreadyTaken => - 'Ce nom d\'utilisateur est deja prit'; + 'Ce nom d\'utilisateur est déjà pris'; @override - String get fullName => 'Nom complet'; + String get creatingYourAccount => 'Création de votre compte'; @override - String get creatingYourAccount => 'Nous creons votre compte'; + String get emailNameTooShort => 'Nom d\'e-mail trop court'; @override - String get welcomeToIS => 'Bienvenue sur IS'; + String get loadingYourDashboard => 'Chargement de votre tableau de bord'; @override - String get welcomeConnectOrCreateAccount => - 'Connectez un compte existant ou creez en un nouveau pour utiliser IS'; + String get couldNotGetSchoolApplicationForms => + 'Impossible de récupérer les formulaires de demande d\'école'; @override - String get connectAccount => 'Connectez votre compte'; + String get welcomeExcl => 'Bienvenue !'; @override - String get welcomeExcl => 'Bienvenue!'; + String get schoolApplication => 'Candidature scolaire'; @override - String get phoneNumber => 'Numero de telephone'; + String get noDescriptionProvided => 'Aucune description fournie.'; @override - String get termsAndConditions => '#Terms and Conditions'; + String get errorLoadingTerms => + 'Erreur de chargement des conditions d\'utilisation'; @override - String get continueGt => 'Continuer >'; + String get areWeGoing => 'On y va ?'; @override - String get passwordShouldHaveAtleast8Characters => - 'Le mot-de-passe doit faire au moins 8 characteres de long'; + String get editProfile => 'Modifier le profil'; + + @override + String get reenterPassword => 'Retapez votre mot de passe'; @override - String get createAccount => 'Creez un compte'; + String get username => 'Nom d\'utilisateur'; + + @override + String get loginInformations => 'Informations de connexion'; + + @override + String get invalidCharacterInEmail => 'Caractère invalide dans l\'e-mail'; + + @override + String get exploreSchools => 'Explorer les écoles'; + + @override + String get invalidEmailDomainSecondPart => + 'Domaine de l\'e-mail invalide (deuxième partie)'; @override - String get errorLoadingTerms => '#Error loading terms of service'; + String get iHaveReadAndAgreeToTheTermsAndConditions => + 'J\'ai lu et j\'accepte les conditions d\'utilisation.'; + + @override + String get welcomeConnectOrCreateAccount => + 'Connectez un compte existant ou créez-en un nouveau pour commencer votre voyage avec nous'; + + @override + String get phoneNumber => 'Numéro de téléphone'; + + @override + String get termsAndConditions => 'Conditions d\'utilisation'; + + @override + String get addEmailOrPhoneNumber => + 'Ajouter une adresse e-mail ou un numéro de téléphone'; + + @override + String get establishedIn2023 => 'Établi en 2023'; + + @override + String get passwordShouldHaveAtleast8Characters => + '##Le mot de passe doit contenir au moins 8 caractères'; @override - String get wasNotTranslated => '#Was not translated'; + String get createAccount => 'Créer un compte'; @override - String get retry => 'Reessayer'; + String get retry => 'Réessayer'; } diff --git a/lib/models.dart b/lib/models.dart index 6c76718..8326e69 100644 --- a/lib/models.dart +++ b/lib/models.dart @@ -7,3 +7,4 @@ export 'models/thread.dart'; export 'models/class.dart'; export 'models/userrole.dart'; export 'models/school_user.dart'; +export 'models/school_application.dart'; diff --git a/lib/models/school.dart b/lib/models/school.dart index 09ad05f..6450d05 100644 --- a/lib/models/school.dart +++ b/lib/models/school.dart @@ -84,17 +84,20 @@ class School implements Model { Future> getApplicationForms( Session? session, ) async { - final data = await backend.query("v1/school/applications/forschool", { + final data = await backend.query("v1/school/application/forschool", { "school_id": id.toString(), }, session); if (data['status'] < 0) { throw Exception(data['message']); } - return (data['forms'] as List) - .map( - (form) => - SchoolApplicationForm.fromJson(form as Map), - ) - .toList(); + return data['forms'] == null + ? [] + : (data['forms'] as List) + .map( + (form) => SchoolApplicationForm.fromJson( + form as Map, + ), + ) + .toList(); } } diff --git a/lib/models/school_application.dart b/lib/models/school_application.dart index 1aef42a..ecbe77f 100644 --- a/lib/models/school_application.dart +++ b/lib/models/school_application.dart @@ -81,13 +81,15 @@ class SchoolApplicationForm implements Model { description: data['description'] as String?, instructions: data['instructions'] as String?, submittedMessage: data['submitted_message'] as String, - questions: (data['questions'] as List) - .map( - (e) => SchoolApplicationFormQuestion.fromJson( - e as Map, - ), - ) - .toList(), + questions: data['questions'] == null + ? [] + : (data['questions'] as List) + .map( + (e) => SchoolApplicationFormQuestion.fromJson( + e as Map, + ), + ) + .toList(), ); } @override diff --git a/lib/pages/dashboard/blank/dashboard.dart b/lib/pages/dashboard/blank/dashboard.dart index f6e2972..171afe1 100644 --- a/lib/pages/dashboard/blank/dashboard.dart +++ b/lib/pages/dashboard/blank/dashboard.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:ins/appstate.dart'; import 'package:ins/pages/school/explore.dart'; +import 'package:ins/l10n/app_localizations.dart'; class BlankDashboard extends StatelessWidget { final AppState appState; @@ -9,7 +10,10 @@ class BlankDashboard extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Are we going?")), + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.areWeGoing), + leading: null, + ), body: Center( child: SizedBox( width: 500, @@ -19,7 +23,7 @@ class BlankDashboard extends StatelessWidget { children: [ DashboardButton( icon: Icons.edit, - text: "Edit Profile", + text: AppLocalizations.of(context)!.editProfile, color: Colors.blue, onPressed: () { // TODO: Implement edit profile functionality @@ -28,7 +32,7 @@ class BlankDashboard extends StatelessWidget { const SizedBox(height: 20), DashboardButton( icon: Icons.settings, - text: "Edit Preferences", + text: AppLocalizations.of(context)!.editPreferences, color: Colors.orange, onPressed: () { // TODO: Implement edit preferences functionality @@ -37,7 +41,7 @@ class BlankDashboard extends StatelessWidget { const SizedBox(height: 20), DashboardButton( icon: Icons.school, - text: "Browse Schools", + text: AppLocalizations.of(context)!.browseSchools, color: Colors.green, onPressed: () { Navigator.of(context).push( diff --git a/lib/pages/dashboard/dashboard.dart b/lib/pages/dashboard/dashboard.dart index 212ca13..5b86407 100644 --- a/lib/pages/dashboard/dashboard.dart +++ b/lib/pages/dashboard/dashboard.dart @@ -1,6 +1,41 @@ import 'package:flutter/material.dart'; import 'package:ins/appstate.dart'; +import 'package:ins/widgets/imsg.dart'; +import 'package:ins/widgets/loading.dart'; import 'blank/dashboard.dart'; +import 'package:ins/l10n/app_localizations.dart'; + +Widget loadingDashboard(BuildContext context, {bool backButton = true}) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.loadingYourDashboard), + leading: backButton ? BackButton() : null, + ), + body: Column( + children: [ + LoadingWidget( + messages: AppLocalizations.of(context)!.waitingMessages.split("|"), + ), + ], + ), + ); +} + +Widget errorLoadingDashboard( + BuildContext context, + String message, { + bool backButton = true, +}) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.loadingYourDashboard), + leading: backButton ? BackButton() : null, + ), + body: Column( + children: [IMsgWidget(icon: Icon(Icons.error), message: Text(message))], + ), + ); +} Future getDashboard(AppState? state) async { state ??= await AppState.load(); @@ -8,7 +43,27 @@ Future getDashboard(AppState? state) async { return BlankDashboard(appState: state); } else { return Scaffold( - appBar: AppBar(leading: BackButton(), title: Text("Nothing here")), + appBar: AppBar( + leading: BackButton(), + title: Builder( + builder: (context) => Text(AppLocalizations.of(context)!.nothingHere), + ), + ), ); } } + +Widget loadDashboard(AppState? state) { + return FutureBuilder( + future: getDashboard(state), + builder: (context, snapshot) { + if (snapshot.hasData) { + return snapshot.data!; + } else if (snapshot.connectionState == ConnectionState.waiting) { + return loadingDashboard(context); + } else { + return errorLoadingDashboard(context, snapshot.error.toString()); + } + }, + ); +} diff --git a/lib/pages/home.dart b/lib/pages/home.dart index ffcc3dc..1398365 100644 --- a/lib/pages/home.dart +++ b/lib/pages/home.dart @@ -46,7 +46,7 @@ Widget getPage() { return _inBlank( IMsgWidget( icon: Icon(Icons.error), - message: Text("Error loading your dashboard"), + message: Text(AppLocalizations.of(context)!.errorLoadingYourDashboard), ), ); } diff --git a/lib/pages/school/apply/home.dart b/lib/pages/school/apply/home.dart new file mode 100644 index 0000000..e0d89c5 --- /dev/null +++ b/lib/pages/school/apply/home.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:ins/appstate.dart'; +import 'package:ins/l10n/app_localizations.dart'; +import 'package:ins/models.dart' as models; +import 'package:ins/widgets/imsg.dart'; + +class SchoolApplyHomePage extends StatelessWidget { + final AppState appState; + final models.School school; + + const SchoolApplyHomePage({ + super.key, + required this.school, + required this.appState, + }); + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.schoolApplication), + ), + body: FutureBuilder>( + future: school.getApplicationForms(appState.session), + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!.isEmpty) { + return Center( + child: Text(AppLocalizations.of(context)!.nothingHere), + ); + } + return _buildPage(context, snapshot.data!); + } else if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else { + return IMsgWidget( + icon: const Icon(Icons.error, size: 200, color: Colors.red), + message: Text( + "${AppLocalizations.of(context)!.couldNotGetSchoolApplicationForms}: ${snapshot.error}", + style: Theme.of( + context, + ).textTheme.bodyLarge?.copyWith(color: Colors.red), + ), + ); + } + }, + ), + ); + } + + Widget _buildPage( + BuildContext context, + List forms, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 8.0), + child: Text( + AppLocalizations.of(context)!.whatDoYouWantToApplyFor, + style: Theme.of( + context, + ).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold), + ), + ), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.only(top: 8.0), + itemCount: forms.length, + itemBuilder: (BuildContext context, int index) { + final form = forms[index]; + return Card( + margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () { + // TODO: Handle form selection and navigation + }, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + form.title, + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 8), + if (form.description != null && + form.description!.isNotEmpty) + Text( + form.description!, + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium, + ) + else + Text( + AppLocalizations.of(context)!.noDescriptionProvided, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith( + fontStyle: FontStyle.italic, + color: Colors.grey, + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ], + ); + } +} + diff --git a/lib/pages/school/explore.dart b/lib/pages/school/explore.dart index 71e77e6..ee663c4 100644 --- a/lib/pages/school/explore.dart +++ b/lib/pages/school/explore.dart @@ -5,6 +5,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:ins/appstate.dart'; import 'package:ins/widgets/loading.dart'; import 'school_profile.dart'; +import 'package:ins/l10n/app_localizations.dart'; class SchoolExplorePage extends StatefulWidget { final AppState appState; @@ -22,7 +23,10 @@ class _SchoolExplorePageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: Text("Explore Schools"), elevation: 0), + appBar: AppBar( + title: Text(AppLocalizations.of(context)!.exploreSchools), + elevation: 0, + ), body: FutureBuilder>( future: models.School.getAllSchools( widget.appState.session, @@ -71,7 +75,7 @@ class _SchoolExplorePageState extends State { ), SizedBox(height: 30), Text( - "No school found", + AppLocalizations.of(context)!.noSchoolFound, style: Theme.of(context).textTheme.displayLarge, ), ], diff --git a/lib/pages/school/school_profile.dart b/lib/pages/school/school_profile.dart index 2f945ed..db8c59b 100644 --- a/lib/pages/school/school_profile.dart +++ b/lib/pages/school/school_profile.dart @@ -3,6 +3,8 @@ import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:ins/models.dart' as models; import 'package:ins/appstate.dart'; +import 'package:ins/l10n/app_localizations.dart'; +import 'package:ins/pages/school/apply/home.dart'; class SchoolProfilePage extends StatelessWidget { final models.School school; @@ -41,9 +43,9 @@ class SchoolProfilePage extends StatelessWidget { children: [ _buildAppBar(context, onWideLayout: true), const SizedBox(height: 40), - _buildSchoolHeader(onWideLayout: true), + _buildSchoolHeader(context, onWideLayout: true), const SizedBox(height: 40), - _buildDescription(onWideLayout: true), + _buildDescription(context, onWideLayout: true), ], ), ), @@ -57,7 +59,7 @@ class SchoolProfilePage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( - "At a Glance", + AppLocalizations.of(context)!.atAGlance, style: GoogleFonts.poppins( fontSize: 22, fontWeight: FontWeight.bold, @@ -66,7 +68,7 @@ class SchoolProfilePage extends StatelessWidget { const SizedBox(height: 24), _buildStatsRow(context, onWideLayout: true), const Divider(height: 48, thickness: 0.5), - _buildAdditionalInfo(onWideLayout: true), + _buildAdditionalInfo(context, onWideLayout: true), const Spacer(), _buildApplyButton(context), ], @@ -94,13 +96,13 @@ class SchoolProfilePage extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - _buildSchoolHeader(), + _buildSchoolHeader(context), const SizedBox(height: 32), _buildStatsRow(context), const Divider(height: 48, thickness: 0.5), - _buildDescription(), + _buildDescription(context), const SizedBox(height: 32), - _buildAdditionalInfo(), + _buildAdditionalInfo(context), const SizedBox(height: 32), _buildApplyButton(context), ], @@ -111,12 +113,15 @@ class SchoolProfilePage extends StatelessWidget { ); } - Widget _buildAdditionalInfo({bool onWideLayout = false}) { + Widget _buildAdditionalInfo( + BuildContext context, { + bool onWideLayout = false, + }) { final content = Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Contact Information", + AppLocalizations.of(context)!.contactInformation, style: GoogleFonts.poppins(fontSize: 18, fontWeight: FontWeight.w600), ), const SizedBox(height: 16), @@ -148,9 +153,14 @@ class SchoolProfilePage extends StatelessWidget { Widget _buildApplyButton(BuildContext context) { return FilledButton.icon( - onPressed: null, // TODO: Implement application logic + onPressed: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + SchoolApplyHomePage(school: school, appState: appState), + ), + ), icon: const Icon(Icons.edit_document), - label: const Text("Apply Now"), + label: Text(AppLocalizations.of(context)!.applyNow), style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20), textStyle: GoogleFonts.poppins( @@ -161,9 +171,11 @@ class SchoolProfilePage extends StatelessWidget { ); } - Widget _buildDescription({bool onWideLayout = false}) { + Widget _buildDescription(BuildContext context, {bool onWideLayout = false}) { return MarkdownBody( - data: school.description ?? "No description provided.", + data: + school.description ?? + AppLocalizations.of(context)!.noDescriptionProvided, styleSheet: MarkdownStyleSheet( p: GoogleFonts.poppins( fontSize: 16, @@ -209,7 +221,7 @@ class SchoolProfilePage extends StatelessWidget { ); } - Widget _buildSchoolHeader({bool onWideLayout = false}) { + Widget _buildSchoolHeader(BuildContext context, {bool onWideLayout = false}) { return Row( children: [ Hero( @@ -247,7 +259,7 @@ class SchoolProfilePage extends StatelessWidget { ), const SizedBox(height: 4), Text( - "Established in 2023", // Placeholder + AppLocalizations.of(context)!.establishedIn2023, // Placeholder style: GoogleFonts.poppins(fontSize: 16, color: Colors.black54), ), ], @@ -283,9 +295,13 @@ class SchoolProfilePage extends StatelessWidget { Widget _buildStatsRow(BuildContext context, {bool onWideLayout = false}) { final stats = [ - _buildStatItem(Icons.star, "Rating", "4.4"), - _buildStatItem(Icons.people, "Students", "0"), - _buildStatItem(Icons.location_city, "Campus", "Online"), + _buildStatItem(Icons.star, AppLocalizations.of(context)!.rating, "4.4"), + _buildStatItem(Icons.people, AppLocalizations.of(context)!.students, "0"), + _buildStatItem( + Icons.location_city, + AppLocalizations.of(context)!.campus, + AppLocalizations.of(context)!.online, + ), ]; return IntrinsicHeight( @@ -302,158 +318,3 @@ class SchoolProfilePage extends StatelessWidget { ); } } - - - -//IconData _getFormIcon(models.SchoolApplicationForm form) { -// return Icons.school; -// /* -// switch (form.type.toLowerCase()) { -// case 'transfer': -// return Icons.swap_horiz; -// case 'international': -// return Icons.language; -// case 'graduate': -// return Icons.school; -// case 'scholarship': -// return Icons.attach_money; -// default: -// return Icons.description; -// }*/ -//} - -//Future _selectApplicationForm( -//BuildContext context, -//models.School school, -//models.Session session, -//models.User user, -//) async { -// final forms = await school.getApplicationForms(); -// if (!context.mounted) return; -// -// showModalBottomSheet( -// context: context, -// isScrollControlled: true, -// backgroundColor: Colors.transparent, -// barrierColor: Colors.black.withOpacity(0.4), -// builder: (context) => Container( -// decoration: BoxDecoration( -// color: Theme.of(context).scaffoldBackgroundColor, -// borderRadius: const BorderRadius.vertical(top: Radius.circular(25)), -// ), -// child: Padding( -// padding: const EdgeInsets.only( -// top: 12, -// left: 16, -// right: 16, -// bottom: 24, -// ), -// child: Column( -// mainAxisSize: MainAxisSize.min, -// children: [ -// Center( -// child: Container( -// width: 40, -// height: 4, -// decoration: BoxDecoration( -// color: Colors.grey[400], -// borderRadius: BorderRadius.circular(2), -// ), -// ), -// ), -// const SizedBox(height: 16), -// Text( -// "Select application form", -// style: GoogleFonts.poppins( -// fontSize: 22, -// fontWeight: FontWeight.w600, -// color: Theme.of(context).primaryColor, -// ), -// ), -// const SizedBox(height: 24), -// Flexible( -// child: ListView.separated( -// shrinkWrap: true, -// physics: const ClampingScrollPhysics(), -// itemCount: forms.length, -// separatorBuilder: (_, __) => const Divider(height: 1), -// itemBuilder: (context, index) { -// final form = forms[index]; -// return Material( -// color: Colors.transparent, -// child: InkWell( -// borderRadius: BorderRadius.circular(12), -// onTap: () { -// Navigator.pop(context); -// launchApplicationForm( -// context, -// school, -// session, -// user, -// form, -// ); -// }, -// child: Container( -// padding: const EdgeInsets.symmetric( -// vertical: 16, -// horizontal: 20, -// ), -// child: Row( -// children: [ -// Container( -// padding: const EdgeInsets.all(12), -// decoration: BoxDecoration( -// color: Theme.of( -// context, -// ).primaryColor.withOpacity(0.1), -// borderRadius: BorderRadius.circular(12), -// ), -// child: Icon( -// _getFormIcon(form), -// color: Theme.of(context).focusColor, -// size: 28, -// ), -// ), -// const SizedBox(width: 20), -// Expanded( -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Text( -// form.title, -// style: GoogleFonts.poppins( -// fontSize: 16, -// fontWeight: FontWeight.w600, -// ), -// ), -// Padding( -// padding: const EdgeInsets.only(top: 4), -// child: Text( -// form.description, -// style: GoogleFonts.poppins( -// fontSize: 14, -// color: Colors.grey[600], -// ), -// maxLines: 2, -// ), -// ), -// ], -// ), -// ), -// Icon(Icons.chevron_right, color: Colors.grey[400]), -// ], -// ), -// ), -// ), -// ); -// }, -// ), -// ), -// ], -// ), -// ), -// ), -// ); -//} - - diff --git a/lib/pages/sign/signup/extlinked.dart b/lib/pages/sign/signup/extlinked.dart index 1a7324b..5d8361f 100644 --- a/lib/pages/sign/signup/extlinked.dart +++ b/lib/pages/sign/signup/extlinked.dart @@ -122,7 +122,7 @@ class _ExtraLinkedPageState extends State { if (_emailController.text.isEmpty) { _emailError = null; } else { - _emailError = chechEmail(_emailController.text); + _emailError = chechEmail(context, _emailController.text); } }); } diff --git a/lib/pages/sign/signup/submit.dart b/lib/pages/sign/signup/submit.dart index 872e814..eecee67 100644 --- a/lib/pages/sign/signup/submit.dart +++ b/lib/pages/sign/signup/submit.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:ins/l10n/app_localizations.dart'; +import 'package:ins/pages/dashboard/dashboard.dart'; import 'package:ins/widgets/imsg.dart'; import 'form.dart'; import 'package:ins/widgets/loading.dart'; @@ -74,5 +75,9 @@ class SubmitingPage extends StatelessWidget { ); } - void _openDashboard(BuildContext context) {} + void _openDashboard(BuildContext context) { + Navigator.of(context).pushReplacement( + MaterialPageRoute(builder: (context) => loadDashboard(null)), + ); + } } diff --git a/lib/pages/sign/signup/terms.dart b/lib/pages/sign/signup/terms.dart index 904f0b4..49d1a28 100644 --- a/lib/pages/sign/signup/terms.dart +++ b/lib/pages/sign/signup/terms.dart @@ -131,8 +131,10 @@ class _TermsWidgetState extends State { ), const SizedBox(height: 16), CheckboxListTile( - title: const Text( - "I have read and agree to the terms and conditions.", + title: Text( + AppLocalizations.of( + context, + )!.iHaveReadAndAgreeToTheTermsAndConditions, ), value: _termsAccepted, onChanged: (bool? value) { @@ -162,7 +164,7 @@ class _TermsWidgetState extends State { padding: const EdgeInsets.symmetric(vertical: 16.0), textStyle: Theme.of(context).textTheme.labelLarge, ), - child: Text("Accept & Continue"), + child: Text(AppLocalizations.of(context)!.acceptContinue), ), ], ), diff --git a/lib/utils/email.dart b/lib/utils/email.dart index e9e315b..cea3aee 100644 --- a/lib/utils/email.dart +++ b/lib/utils/email.dart @@ -1,15 +1,29 @@ -String? chechEmail(String email) { +import 'package:flutter/material.dart'; +import 'package:ins/l10n/app_localizations.dart'; + +String? chechEmail(BuildContext context, String email) { final match = RegExp("[^a-zA-Z0-9@.]").firstMatch("email"); - if (match != null) return "Invalid character in email ${match[0]}"; - if (!email.contains("@")) return "Email should contain '@'"; + if (match != null) { + return AppLocalizations.of(context)!.invalidCharacterInEmail + + match[0].toString(); + } + if (!email.contains("@")) { + return AppLocalizations.of(context)!.emailShouldContain; + } final parts = email.split("@"); if (parts.length != 2) { - return "Email should contain 1 '@'"; + return AppLocalizations.of(context)!.emailShouldContain1; + } + if (parts[0].length < 2) { + return AppLocalizations.of(context)!.emailNameTooShort; + } + if (!parts[1].contains(".")) { + return AppLocalizations.of(context)!.invalidEmailDomainsecondPart; } - if (parts[0].length < 2) return "Email name too short"; - if (!parts[1].contains(".")) return "Invalid email domain(second part)"; for (var element in parts[1].split(".")) { - if (element.isEmpty) return "Invalid domain component"; + if (element.isEmpty) { + return AppLocalizations.of(context)!.invalidDomainComponent; + } } return null; }