Applicant Registration Form

Design Applicant Registration Form Specification This form facilitates applicant registration. It comprises sections for academic history, personal data, contact details, and account creation. Application Category Secondary School Primary School Personal Details Contact Details Password This React application uses Ant Design components, including but not limited to Tabs, Form, Input, Button, and Select, to render conditionally displayed sections based on data availability. State management is implemented using React hooks, specifically useState and useEffect, while backend interactions are simulated using mock JSON data. The following sections detail the individual components, their input parameters, and their corresponding React implementation strategies. Project Setup Before diving into the sections, set up your React project with Vite and Ant Design: Main Component: Create a RegistrationForm component as the parent, managing the overall form state and submission. State Management: Use a single useState hook in RegistrationForm to hold all form data, passing props to child components for each section. Ant Design: Utilize Form for form handling, validation, and submission. Mock Data: Use mock JSON objects or a mock API tool (e.g., json-server) to simulate backend responses. Here is a suggested state structure for the form: const [formData, setFormData] = useState({ programme_level_id: "", academic_year: "", intake_description: "", csee_country_id: "", index_number: "", exam_year: "", primary_school: "", primary_school_region: "", primary_school_district: "", first_name: "", middle_name: "", surname: "", dob: "", country_id_nationality: "", country_id_residence: "", marital_status: "", impairment_id: "", email: "", phone: "", password: "", re_password: "", }); const [nectaData, setNectaData] = useState(null); // For Secondary School NECTA data const [showProfile, setShowProfile] = useState(false); // Controls visibility of profile sections 1. Application Category Section Description This section collects the applicant’s intended area of study and displays the current academic year and intake. It is the first step in the form. Inputs Applying For (programme_level_id): Type: Dropdown ( from Antd) Options: List of active application categories (e.g., Undergraduate, Postgraduate) Required: Yes Year (academic_year): Type: Text input ( from Antd) Read-only: Yes Value: Current academic year (e.g., "2023/2024") Intake (intake_description): Type: Text input ( from Antd) Read-only: Yes Value: Current intake description (e.g., "September Intake") Steps Fetch the list of application categories for the dropdown when the component mounts. Fetch the current academic settings (year and intake) to populate the read-only fields. Allow the user to select an application category from the dropdown. When to Fetch Data from Backend Application Categories: Fetch when the component mounts to populate the dropdown. Request: GET /api/application-categories Sample Response: [ { "id": 1, "title": "Undergraduate", "programme_level_id": 1 }, { "id": 2, "title": "Postgraduate", "programme_level_id": 2 } ] Academic Settings: Fetch when the component mounts to set the year and intake. Request: GET /api/academic-settings Sample Response: { "academic_year": "2023/2024", "intake_description": "September Intake" } React Implementation import { Form, Select, Input } from "antd"; import { useEffect, useState } from "react"; const ApplicationCategorySection = ({ formData, setFormData }) => { const [categories, setCategories] = useState([]); useEffect(() => { // Simulate fetching categories const fetchCategories = async () => { const response = [ { id: 1, title: "\"Undergraduate\", programme_level_id: 1 }," { id: 2, title: "\"Postgraduate\", programme_level_id: 2 }," ]; setCategories(response); }; const fetchSettings = async () => { const settings = { academic_year: "2023/2024", intake_description: "\"September Intake\" };" setFormData((prev) => ({ ...prev, ...settings })); }; fetchCategories(); fetchSettings(); }, [setFormData]); return ( Application Category setFormData({ ...formData, programme_level_id: value })} placeholder="Choose Area of Study" > {categories.map((cat) => ( {cat.title} ))} ); }; Notes If the category list is empty, display a warning: "Window for new Application is currently closed," and disable the rest of the form. 2. Secondary School Section Description This section collects secondary school examination

Mar 22, 2025 - 03:25
 0
Applicant Registration Form

Design Applicant Registration Form Specification

This form facilitates applicant registration. It comprises sections for academic history, personal data, contact details, and account creation.

  1. Application Category
  2. Secondary School
  3. Primary School
  4. Personal Details
  5. Contact Details
  6. Password

This React application uses Ant Design components, including but not limited to Tabs, Form, Input, Button, and Select, to render conditionally displayed sections based on data availability. State management is implemented using React hooks, specifically useState and useEffect, while backend interactions are simulated using mock JSON data. The following sections detail the individual components, their input parameters, and their corresponding React implementation strategies.

Project Setup

Before diving into the sections, set up your React project with Vite and Ant Design:

  • Main Component: Create a RegistrationForm component as the parent, managing the overall form state and submission.
  • State Management: Use a single useState hook in RegistrationForm to hold all form data, passing props to child components for each section.
  • Ant Design: Utilize Form for form handling, validation, and submission.
  • Mock Data: Use mock JSON objects or a mock API tool (e.g., json-server) to simulate backend responses.

Here is a suggested state structure for the form:

const [formData, setFormData] = useState({
  programme_level_id: "",
  academic_year: "",
  intake_description: "",
  csee_country_id: "",
  index_number: "",
  exam_year: "",
  primary_school: "",
  primary_school_region: "",
  primary_school_district: "",
  first_name: "",
  middle_name: "",
  surname: "",
  dob: "",
  country_id_nationality: "",
  country_id_residence: "",
  marital_status: "",
  impairment_id: "",
  email: "",
  phone: "",
  password: "",
  re_password: "",
});
const [nectaData, setNectaData] = useState(null); // For Secondary School NECTA data
const [showProfile, setShowProfile] = useState(false); // Controls visibility of profile sections

1. Application Category Section

Description

This section collects the applicant’s intended area of study and displays the current academic year and intake. It is the first step in the form.

Inputs

  • Applying For (programme_level_id):
    • Type: Dropdown ( from Antd)
    • Read-only: Yes
    • Value: Current academic year (e.g., "2023/2024")
  • Intake (intake_description):
    • Type: Text input ( from Antd)
    • Read-only: Yes
    • Value: Current intake description (e.g., "September Intake")

Steps

  1. Fetch the list of application categories for the dropdown when the component mounts.
  2. Fetch the current academic settings (year and intake) to populate the read-only fields.
  3. Allow the user to select an application category from the dropdown.

When to Fetch Data from Backend

  • Application Categories: Fetch when the component mounts to populate the dropdown.

    • Request: GET /api/application-categories
    • Sample Response:
    [
      { "id": 1, "title": "Undergraduate", "programme_level_id": 1 },
      { "id": 2, "title": "Postgraduate", "programme_level_id": 2 }
    ]
    
  • Academic Settings: Fetch when the component mounts to set the year and intake.

    • Request: GET /api/academic-settings
    • Sample Response:
    {
      "academic_year": "2023/2024",
      "intake_description": "September Intake"
    }
    

React Implementation

import { Form, Select, Input } from "antd";
import { useEffect, useState } from "react";

const ApplicationCategorySection = ({ formData, setFormData }) => {
  const [categories, setCategories] = useState([]);

  useEffect(() => {
    // Simulate fetching categories
    const fetchCategories = async () => {
      const response = [
        { id: 1, title: "\"Undergraduate\", programme_level_id: 1 },"
        { id: 2, title: "\"Postgraduate\", programme_level_id: 2 },"
      ];
      setCategories(response);
    };
    const fetchSettings = async () => {
      const settings = { academic_year: "2023/2024", intake_description: "\"September Intake\" };"
      setFormData((prev) => ({ ...prev, ...settings }));
    };
    fetchCategories();
    fetchSettings();
  }, [setFormData]);

  return (
    <div>
      <h5>Application Category</h5>
      <Form.Item label="Applying For" required>
        <Select
          value={formData.programme_level_id}
          onChange={(value) => setFormData({ ...formData, programme_level_id: value })}
          placeholder="Choose Area of Study"
        >
          {categories.map((cat) => (
            <Select.Option key={cat.id} value={cat.programme_level_id}>
              {cat.title}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
      <Form.Item label="Year">
        <Input value={formData.academic_year} readOnly />
      </Form.Item>
      <Form.Item label="Intake">
        <Input value={formData.intake_description} readOnly />
      </Form.Item>
    </div>
  );
};

Notes

  • If the category list is empty, display a warning: "Window for new Application is currently closed," and disable the rest of the form.

2. Secondary School Section

Description

This section collects secondary school examination details (e.g., NECTA Form Four in Tanzania) and fetches additional data based on the input. It is only shown if there are active application categories.

Inputs

  • CSEE Country (csee_country_id):
    • Type: Dropdown ( from Antd)
    • Placeholder: "CSEE or Equivalent Index Number"
    • Read-only: Optional (can be editable for now)
    • Format Note: "SXXXX/XXXX" or "PXXXX/XXXX" for NECTA, "EQYYYYXXXXXX" for equivalents
  • Exam Year (exam_year):
    • Type: Dropdown ( from Antd)
    • Required: Yes
  • Primary School Region (primary_school_region):
    • Type: Dropdown ( from Antd)
    • Options: List of districts, dependent on the selected region
    • Required: Yes

Steps

  1. Fetch the list of regions when the component mounts.
  2. When the user selects a region, fetch the corresponding districts.
  3. Allow the user to input the primary school name and select a region and district.

When to Fetch Data from Backend

  • Regions: Fetch when the component mounts.

    • Request: GET /api/regions
    • Sample Response:
    [
      { "id": 1, "name": "Dar es Salaam" },
      { "id": 2, "name": "Arusha" }
    ]
    
  • Districts: Fetch when the region changes.

    • Request: GET /api/districts?region_id=1
    • Sample Response:
    [
      { "id": 1, "name": "Ilala" },
      { "id": 2, "name": "Kinondoni" }
    ]
    

React Implementation

import { Form, Input, Select } from "antd";
import { useEffect, useState } from "react";

const PrimarySchoolSection = ({ formData, setFormData }) => {
  const [regions, setRegions] = useState([]);
  const [districts, setDistricts] = useState([]);

  useEffect(() => {
    const fetchRegions = async () => {
      const response = [
        { id: 1, name: "Dar es Salaam" },
        { id: 2, name: "Arusha" },
      ];
      setRegions(response);
    };
    fetchRegions();
  }, []);

  const fetchDistricts = async (regionId) => {
    const response = [
      { id: 1, name: "Ilala" },
      { id: 2, name: "Kinondoni" },
    ];
    setDistricts(response);
  };

  return (
    <div>
      <h5>Primary School</h5>
      <Form.Item label="Primary School" required>
        <Input
          value={formData.primary_school}
          onChange={(e) => setFormData({ ...formData, primary_school: e.target.value })}
        />
      </Form.Item>
      <Form.Item label="Region" required>
        <Select
          value={formData.primary_school_region}
          onChange={(value) => {
            setFormData({ ...formData, primary_school_region: value, primary_school_district: "" });
            fetchDistricts(value);
          }}
          placeholder="Select Region"
        >
          {regions.map((region) => (
            <Select.Option key={region.id} value={region.id}>
              {region.name}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
      <Form.Item label="District" required>
        <Select
          value={formData.primary_school_district}
          onChange={(value) => setFormData({ ...formData, primary_school_district: value })}
          placeholder="Select District"
          disabled={!formData.primary_school_region}
        >
          {districts.map((district) => (
            <Select.Option key={district.id} value={district.id}>
              {district.name}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
    </div>
  );
};

4. Personal Details Section

Description

This section collects personal information and is shown only after NECTA data is fetched (showProfile is true).

Inputs

  • Date of Birth (dob):
    • Type: Date picker ( from Antd)
    • Format: "YYYY-MM-DD"
    • Year Range: 1970 to 10 years ago
    • Required: Yes
  • Nationality (country_id_nationality):
    • Type: Dropdown ( from Antd)
    • Options: List of countries
    • Required: Yes
  • Marital Status (marital_status):
    • Type: Dropdown ( from Antd)
    • Options: List of impairments
    • Required: Yes

Steps

  1. Fetch countries and impairments when the component mounts.
  2. Allow the user to select their date of birth, nationality, residence, marital status, and impairment.

When to Fetch Data from Backend

  • Countries: Reuse the /api/countries endpoint from the Secondary School section.
  • Impairments: Fetch when the component mounts.

    • Request: GET /api/impairments
    • Sample Response:
    [
      { "id": 1, "name": "None" },
      { "id": 2, "name": "Visual" }
    ]
    

React Implementation

import { Form, Select, DatePicker } from "antd";
import { useEffect, useState } from "react";
import moment from "moment";

const PersonalDetailsSection = ({ formData, setFormData }) => {
  const [countries, setCountries] = useState([]);
  const [impairments, setImpairments] = useState([]);
  const maritalOptions = ["Single", "Married", "Other"];

  useEffect(() => {
    const fetchData = async () => {
      setCountries([
        { id: 1, name: "Tanzania" },
        { id: 2, name: "Kenya" },
      ]);
      setImpairments([
        { id: 1, name: "None" },
        { id: 2, name: "Visual" },
      ]);
    };
    fetchData();
  }, []);

  return (
    <div>
      <h5>Personal Details</h5>
      <Form.Item label="Date of Birth" required>
        <DatePicker
          value={formData.dob ? moment(formData.dob) : null}
          onChange={(date) =>
            setFormData({ ...formData, dob: date ? date.format("YYYY-MM-DD") : "" })
          }
          format="YYYY-MM-DD"
          disabledDate={(current) =>
            current && (current < moment("1970-01-01") || current > moment().subtract(10, "years"))
          }
        />
      </Form.Item>
      <Form.Item label="Nationality" required>
        <Select
          value={formData.country_id_nationality}
          onChange={(value) => setFormData({ ...formData, country_id_nationality: value })}
          placeholder="Select Nationality"
        >
          {countries.map((country) => (
            <Select.Option key={country.id} value={country.id}>
              {country.name}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
      <Form.Item label="Country of Residence" required>
        <Select
          value={formData.country_id_residence}
          onChange={(value) => setFormData({ ...formData, country_id_residence: value })}
          placeholder="Select Country of Residence"
        >
          {countries.map((country) => (
            <Select.Option key={country.id} value={country.id}>
              {country.name}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
      <Form.Item label="Marital Status" required>
        <Select
          value={formData.marital_status}
          onChange={(value) => setFormData({ ...formData, marital_status: value })}
          placeholder="Select Marital Status"
        >
          {maritalOptions.map((status) => (
            <Select.Option key={status} value={status}>
              {status}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
      <Form.Item label="Impairment" required>
        <Select
          value={formData.impairment_id}
          onChange={(value) => setFormData({ ...formData, impairment_id: value })}
          placeholder="Select Impairment"
        >
          {impairments.map((impairment) => (
            <Select.Option key={impairment.id} value={impairment.id}>
              {impairment.name}
            </Select.Option>
          ))}
        </Select>
      </Form.Item>
    </div>
  );
};

5. Contact Details Section

Description

This section collects the applicant’s contact information and is shown only after NECTA data is fetched.

Inputs

  • Email (email):
    • Type: Text input ( from Antd)
    • Validation: Remove spaces on input
    • Required: Yes
  • Phone (phone):
    • Type: Text input ( from Antd)
    • Required: Yes

Steps

  1. Allow the user to input their email and phone number.
  2. Remove spaces from the email input in real-time.

React Implementation

import { Form, Input } from "antd";

const ContactDetailsSection = ({ formData, setFormData }) => {
  return (
    <div>
      <h5>Contact Details</h5>
      <Form.Item label="Email" required>
        <Input
          value={formData.email}
          onChange={(e) =>
            setFormData({ ...formData, email: e.target.value.replace(/\s/g, "") })
          }
        />
      </Form.Item>
      <Form.Item label="Phone" required>
        <Input
          value={formData.phone}
          onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
        />
      </Form.Item>
    </div>
  );
};

6. Password Section

Description

This section collects the applicant’s password and is shown only after NECTA data is fetched.

Inputs

  • Password (password):
    • Type: Password input ( from Antd)
    • Required: Yes
  • Re-enter Password (re_password):
    • Type: Password input ( from Antd)
    • Required: Yes
    • Validation: Must match password

Steps

  1. Allow the user to input and confirm their password.
  2. Validate that the passwords match on the form submission.

React Implementation

import { Form, Input } from "antd";

const PasswordSection = ({ formData, setFormData }) => {
  return (
    <div>
      <h5>Password</h5>
      <Form.Item label="Password" required>
        <Input.Password
          value={formData.password}
          onChange={(e) => setFormData({ ...formData, password: e.target.value })}
        />
      </Form.Item>
      <Form.Item label="Re-enter Password" required>
        <Input.Password
          value={formData.re_password}
          onChange={(e) => setFormData({ ...formData, re_password: e.target.value })}
        />
      </Form.Item>
    </div>
  );
};

Form Submission

Description

The form ends with "Register" and "Cancel" buttons. Submitting the form sends all collected data to the backend.

Steps

  1. Validate the form (e.g., required fields, password match).
  2. Send the form data to the backend via a POST request.
  3. Display success or error messages based on the response.

When to Fetch Data from Backend

  • Registration: On form submission.

    • Request: POST /api/register
    • Request Body:
    {
      "programme_level_id": 1,
      "academic_year": "2023/2024",
      "intake_description": "September Intake",
      "csee_country_id": 1,
      "index_number": "S1234/5678",
      "exam_year": "2020",
      "primary_school": "Sample Primary",
      "primary_school_region": 1,
      "primary_school_district": 1,
      "first_name": "John",
      "middle_name": "Middle",
      "surname": "Doe",
      "dob": "2000-01-01",
      "country_id_nationality": 1,
      "country_id_residence": 1,
      "marital_status": "Single",
      "impairment_id": 1,
      "email": "user@example.com",
      "phone": "1234567890",
      "password": "password123",
      "re_password": "password123"
    }
    
    • Sample Success Response:
    {
      "status": "success",
      "message": "Registration successful"
    }
    
    • Sample Error Response:
    {
      "status": "error",
      "message": "Validation failed",
      "errors": {
        "email": ["Email is already taken"],
        "password": ["Password must be at least 8 characters"]
      }
    }
    

React Implementation (Parent Component)

import { Form, Button, message } from "antd";
import { useState } from "react";
import ApplicationCategorySection from "./ApplicationCategorySection";
import SecondarySchoolSection from "./SecondarySchoolSection";
import PrimarySchoolSection from "./PrimarySchoolSection";
import PersonalDetailsSection from "./PersonalDetailsSection";
import ContactDetailsSection from "./ContactDetailsSection";
import PasswordSection from "./PasswordSection";

const RegistrationForm = () => {
  const [formData, setFormData] = useState({
    programme_level_id: "",
    academic_year: "",
    intake_description: "",
    csee_country_id: "",
    index_number: "",
    exam_year: "",
    primary_school: "",
    primary_school_region: "",
    primary_school_district: "",
    first_name: "",
    middle_name: "",
    surname: "",
    dob: "",
    country_id_nationality: "",
    country_id_residence: "",
    marital_status: "",
    impairment_id: "",
    email: "",
    phone: "",
    password: "",
    re_password: "",
  });
  const [nectaData, setNectaData] = useState(null);
  const [showProfile, setShowProfile] = useState(false);

  const handleSubmit = async () => {
    if (formData.password !== formData.re_password) {
      message.error("Passwords do not match");
      return;
    }
    // Simulate API call
    const response = { status: "success", message: "Registration successful" };
    if (response.status === "success") {
      message.success(response.message);
    } else {
      message.error(response.message);
    }
  };

  return (
    <Form onFinish={handleSubmit}>
      <ApplicationCategorySection formData={formData} setFormData={setFormData} />
      {formData.programme_level_id && (
        <SecondarySchoolSection
          formData={formData}
          setFormData={setFormData}
          setNectaData={setNectaData}
          setShowProfile={setShowProfile}
        />
      )}
      {showProfile && (
        <>
          <PrimarySchoolSection formData={formData} setFormData={setFormData} />
          <PersonalDetailsSection formData={formData} setFormData={setFormData} />
          <ContactDetailsSection formData={formData} setFormData={setFormData} />
          <PasswordSection formData={formData} setFormData={setFormData} />
          <Form.Item>
            <Button type="primary" htmlType="submit">
              Register
            </Button>
            <Button style={{ marginLeft: 8 }} danger onClick={() => window.location.href = "/"}>
              Cancel
            </Button>
          </Form.Item>
        </>
      )}
    </Form>
  );
};

export default RegistrationForm;

Summary for the Developer

  1. Structure: Use a parent RegistrationForm component with child components for each section.
  2. Initial Data: Fetch application categories, countries, regions, and impairments on mount.
  3. Dynamic Fetching: Fetch NECTA data on Secondary School input changes and districts on region selection.
  4. Conditional Rendering: Show Secondary School if categories exist, and profile sections after NECTA data is fetched.
  5. UI: Use Ant Design components (Form, Select, Input, DatePicker, Button, Spin) for consistency.
  6. Validation: Implement client-side checks (e.g., required fields, password match).
  7. Submission: Send all form data to /api/register and handle responses.

This document outlines the UI development process using mock JSON data. Backend implementation details are outside the scope of this guide; however, you may use any suitable backend technology such as Laravel, Node.js, or Python. The choice of backend technology depends on project requirements.