JIIT time table website

Home Projects JIIT time table website Export And Sharing Shareable Urls And Configuration Saving

Shareable URLs & Configuration Sharing

This page documents the system's mechanisms for encoding timetable configurations into shareable URLs and managing user-saved configuration presets. This enables users to distribute their schedule setups and quickly switch between multiple configuration profiles.

For information about how schedules are persisted locally between sessions, see State Management (#3.4). For details on how the form handles user input, see Schedule Form & User Input (#4.1).

Overview

The application provides two primary methods for configuration sharing and reuse:

  1. URL-based sharing: Encodes schedule parameters (year, batch, campus, selected subjects) directly into the URL query string, enabling instant sharing via link
  2. Named saved configurations: Stores complete configuration sets in localStorage with user-defined names for quick switching between multiple setups

Both mechanisms integrate with the schedule generation pipeline and include conflict resolution when URL parameters differ from existing cached schedules.

URL Parameter Structure

The system uses the nuqs library to synchronize application state with URL query parameters, providing automatic parsing and type safety.

Parameter Schema

Parameter Type Parser Description
year string parseAsString Academic year (1-4)
batch string parseAsString Batch identifier (e.g., "E1", "E2")
campus string parseAsString Campus code ("62", "128", "BCA")
electiveCount number parseAsInteger Number of elective subjects selected
selectedSubjects string[] parseAsArrayOf(parseAsString) Array of subject codes
isGenerating boolean parseAsBoolean Generation state indicator

Query State Hooks Declaration:

const [_year, setYear] = useQueryState("year", parseAsString.withDefault(""));
const [_batch, setBatch] = useQueryState("batch", parseAsString.withDefault(""));
const [_campus, setCampus] = useQueryState("campus", parseAsString.withDefault(""));
const [_electiveCount, setElectiveCount] = useQueryState("electiveCount", parseAsInteger.withDefault(0));
const [_selectedSubjects, setSelectedSubjects] = useQueryState("selectedSubjects", parseAsArrayOf(parseAsString).withDefault([]));

Example URL Structure

https://example.com/?year=2&batch=E7&campus=62&selectedSubjects=CSE101,CSE102,CSE103

The selectedSubjects parameter is serialized as a comma-separated list when multiple subjects are selected.

URL State Management Architecture

Architecture Diagram

Generating Shareable URLs

Users can generate shareable URLs through two entry points:

1. Share Button on Saved Configurations

Each saved configuration displays a share icon button that opens the share dialog with a pre-populated URL.

Architecture Diagram

Implementation:

The share button handler extracts configuration data and opens the dialog:

onClick={(e) => {
    e.stopPropagation();
    setShareDialogConfig({
        name,
        config: savedConfigs[name],
    });
    setShowShareDialog(true);
}}

2. Share Dialog Component

The share dialog (Dialog component from Radix UI) constructs the shareable URL and provides copy-to-clipboard functionality.

URL Construction Logic

Architecture Diagram

URL Generation Code:

const { year, batch, campus, selectedSubjects } = shareDialogConfig.config;
const params = new URLSearchParams({
    year,
    batch,
    campus,
    selectedSubjects: (selectedSubjects || []).join(","),
});
const url = `${window.location.origin}${window.location.pathname}?${params.toString()}`;

Copy to Clipboard Implementation

The dialog provides two mechanisms for copying the URL:

  1. Modern Clipboard API: Primary method using navigator.clipboard.writeText()
  2. Fallback Method: Uses document.execCommand('copy') for older browsers
try {
    await navigator.clipboard.writeText(url);
} catch (err) {
    // Fallback for older browsers
    const input = document.querySelector("input[type='text'][readonly]") as HTMLInputElement;
    if (input) {
        input.select();
        document.execCommand("copy");
    }
}

Loading Configurations from URL Parameters

The application automatically detects URL parameters on mount and reconciles them with any existing cached schedule.

Auto-Generation Flow

Architecture Diagram

URL Parameter Reading Logic

The system reads URL parameters directly from window.location.search rather than relying on React state to avoid race conditions:

const urlParams = new URLSearchParams(window.location.search);
const year = urlParams.get("year") || "";
const batch = urlParams.get("batch") || "";
const campus = urlParams.get("campus") || "";
const selectedSubjectsRaw = urlParams.getAll("selectedSubjects") || [];
const selectedSubjects = selectedSubjectsRaw
    .flatMap((s) => s.split(","))
    .map((s) => s.trim())
    .filter(Boolean);

Conflict Resolution Flow

When URL parameters are detected but a cached schedule already exists, the system presents the UrlParamsDialog component to resolve the conflict.

Decision Tree

Architecture Diagram

Parameter Comparison Logic

let isSame = false;
try {
    const cachedObj = cachedParams ? JSON.parse(cachedParams) : {};
    isSame =
        cachedObj.year === year &&
        cachedObj.batch === batch &&
        cachedObj.campus === campus &&
        Array.isArray(cachedObj.selectedSubjects) &&
        Array.isArray(selectedSubjects) &&
        cachedObj.selectedSubjects.length === selectedSubjects.length &&
        cachedObj.selectedSubjects.every((s: string) =>
            selectedSubjects.includes(s)
        );
} catch {}

UrlParamsDialog Component

The dialog component presents three resolution options:

Option Action Handler
Generate New Schedule Replaces cached schedule with URL parameters onOverridehandleUrlParamsOverride
Use as Form Prefill Populates form fields without auto-generating onPrefillhandleUrlParamsPrefill
View Existing Schedule Dismisses dialog, keeps cached schedule onViewExistinghandleUrlParamsViewExisting

Architecture Diagram

Handler Implementations

Override Handler:

const handleUrlParamsOverride = async () => {
    if (!urlParamsData) return;
    setShowUrlParamsDialog(false);
    await handleFormSubmit({
        year: urlParamsData.year,
        batch: urlParamsData.batch,
        electives: urlParamsData.selectedSubjects,
        campus: urlParamsData.campus,
    });
};

Prefill Handler:

const handleUrlParamsPrefill = () => {
    if (!urlParamsData) return;
    setYear(urlParamsData.year);
    setBatch(urlParamsData.batch);
    setCampus(urlParamsData.campus);
    setSelectedSubjects(urlParamsData.selectedSubjects);
    setIsFormOpen(true);
    setShowUrlParamsDialog(false);
};

Saved Configurations

The system maintains a collection of named configuration presets persisted in localStorage under the key "classConfigs".

Data Structure

Architecture Diagram

State Management

Initialization from localStorage:

const [savedConfigs, setSavedConfigs] = React.useState<{[key: string]: any}>(() => {
    const configs = localStorage.getItem("classConfigs");
    return configs ? JSON.parse(configs) : {};
});

Persistence to localStorage:

React.useEffect(() => {
    localStorage.setItem("classConfigs", JSON.stringify(savedConfigs));
}, [savedConfigs]);

Configuration Operations

Saving a Configuration

The handleSaveConfig function is passed to ScheduleForm and called when users name and save their current configuration:

const handleSaveConfig = (name: string, configData: any) => {
    setSavedConfigs((prev) => ({ ...prev, [name]: configData }));
};

Loading a Configuration

When a user selects a saved configuration, the system:

  1. Clears any edited schedule data
  2. Updates URL state with config parameters
  3. Closes the form
  4. Triggers automatic schedule generation
const handleSelectConfig = async (name: string) => {
    const config = savedConfigs[name];
    if (!config) return;

    // Clear any existing edited schedule to ensure fresh display
    setEditedSchedule(null);

    // Set the form state first
    setYear(config.year);
    setBatch(config.batch);
    setElectiveCount(config.electiveCount || 0);
    setSelectedSubjects(config.selectedSubjects || []);
    setCampus(config.campus);
    setSelectedConfig(name);

    // Close the form after loading config
    setIsFormOpen(false);

    // Generate schedule automatically with this config
    await handleFormSubmit({
        year: config.year,
        batch: config.batch,
        electives: config.selectedSubjects || [],
        campus: config.campus,
    });
};

Deleting a Configuration

const handleDeleteConfig = (name: string) => {
    setSavedConfigs((prev) => {
        const newConfigs = { ...prev };
        delete newConfigs[name];
        return newConfigs;
    });
    if (selectedConfig === name) setSelectedConfig("");
};

UI Component Structure

The saved configurations UI appears above the schedule form when configurations exist:

Architecture Diagram

Collapsible Animation:

<motion.div
    initial={false}
    animate={{
        height: isConfigOpen ? "auto" : 0,
        opacity: isConfigOpen ? 1 : 0,
    }}
    style={{ overflow: "hidden" }}
    transition={{ duration: 0.4, ease: "easeInOut" }}
>

Share Dialog Implementation

The share dialog is a Radix UI Dialog component that provides a polished interface for copying shareable URLs.

Component Hierarchy

Architecture Diagram

Dialog Content Structure

Header Section:

  • SVG share icon in colored circle
  • DialogTitle: "Share Configuration"
  • DialogDescription: Config name display

URL Input Section:

  • Read-only text input displaying full shareable URL
  • Inline copy button with clipboard icon
  • URL is auto-selected on focus for manual copying

Action Buttons:

  • Cancel: Closes dialog without action
  • Copy Link: Copies URL and closes dialog

Responsive Design

The dialog implements mobile-responsive styling:

  • max-w-md: Maximum width constraint
  • max-h-[90vh]: Prevents overflow on small screens
  • overflow-y-auto: Scrollable content when needed
  • Backdrop blur effect: backdrop-blur-xl

Integration with Schedule Generation

URL parameters trigger the standard schedule generation pipeline through handleFormSubmit:

Architecture Diagram

Parameter Caching

After successful schedule generation, parameters are cached for comparison with future URL loads:

localStorage.setItem(
    "cachedScheduleParams",
    JSON.stringify({
        year,
        batch,
        campus,
        selectedSubjects: electives,
    })
);

Error Handling and Edge Cases

Missing URL Parameters

The auto-generation effect validates all required parameters before triggering generation:

if (
    !autoGeneratedRef.current &&
    _year &&
    _batch &&
    _campus &&
    _selectedSubjects &&
    _selectedSubjects.length > 0
) {
    autoGeneratedRef.current = true;
    handleFormSubmit({...});
}

Subject Code Deduplication

The UrlParamsDialog handles comma-separated subject lists and deduplicates codes:

const allCodes = subjects
    .flatMap((s) => s.split(","))
    .map((s) => s.trim())
    .filter(Boolean);
const uniqueCodes = Array.from(new Set(allCodes));

Empty Configuration Objects

Guards prevent errors when accessing potentially undefined configuration data:

const subjectList =
    typeof mapping === "object" && mapping !== null && mapping[year]?.subjects
        ? mapping[year].subjects
        : [];

Summary

The shareable URL and configuration sharing system provides:

  1. Zero-friction sharing via URL parameters encoded with nuqs
  2. Named configuration presets persisted in localStorage
  3. Intelligent conflict resolution when URL and cache differ
  4. Polished share UI with clipboard integration
  5. Automatic schedule generation from URL parameters
  6. Cross-session persistence of configuration data

This architecture enables users to share schedules with classmates, maintain multiple configuration profiles for different scenarios, and quickly switch between setups without manual re-entry of parameters.