Skip to content

Latest commit

 

History

History

README.md

js/

Landing Leaderboard Dashboard

This directory holds the frontend for Codebloom, which is visible at codebloom.patinanetwork.org.

This is a React 18 + Vite project, where we mainly utilize Mantine to style our UI.

View STYLEGUIDE.md to view best practices.

Structure

Last updated: 02/15/2026

js/
├── patches                                     # holds package patches we need
├── public                                      # holds all our static assets
├── src
│   ├── __mock__                                # mock server routes
│   ├── app                                     # main app routes
│   ├── components                              # shared re-usable components
│   ├── lib
│   │   ├── api                                 # holds our API specific code
│   │   │   ├── common
│   │   │   ├── queries                         # react query / fetcher logic
│   │   │   ├── schema                          # input validation schemas used in the frontend
│   │   │   ├── types                           # autogenerated types from OpenAPI schema
│   │   │   └── utils
│   │   ├── ff                                  # feature flags
│   │   ├── helper                              # stateless helper functions
│   │   ├── hooks                               # custom re-usable react hooks
│   │   ├── queryProvider.tsx                   # ReactQueryProvider
│   │   ├── reporter                            # custom error reporter
│   │   ├── router.tsx                          # routes
│   │   ├── test                                # test utils
│   │   ├── theme.tsx                           # custom Mantine theme
│   ├── main.tsx                                # react entry point
│   ├── patches.ts                              # custom runtime patches we apply via side-effect import

Best Practices

Please go to STYLEGUIDE.md to view more detailed documentation about our Codebloom UI best practices

Typesafety

The Codebloom frontend has a very close relationship with the Codebloom backend.

schema.ts

We use openapi-typescript to introspect the backend's OpenAPI schema endpoint, which will then convert everything into TypeScript and save into a schema.ts file. This file is saved at js/src/lib/api/types/autogen/schema.ts.

We have two main use-cases for the schema file:

  1. We have a helper method called ApiURL (read about here) which helps us maintain full type-safety when passing data between the frontend & backend and vice versa.
  2. All enums sent from the backend are converted into TypeScript enums, which allow us to programmatically define behaviors based on the enums.

These enums are extra special because we can fully generate UI code based on these enums at compile time.

For example, our leaderboard filters are generated by the useFilters, which takes all the enum values and generates a hook object.

Filters

Every single one of these filters are automatically generated from Tag.java

Last updated: 02/15/2026


We actually decided to try something very experimental: generating more complex types based off these enums via the Codebloom backend. This experiment is currently inside of ComplexJSTypesGenerator.java which currently generates the current file:

/**
 * This file was generated by the Codebloom backend.
 * DO NOT EDIT THIS FILE MANUALLY!!!
 */
import { Tag } from "@/lib/api/types/schema";

export const PARENT_TAGS_TO_CHILD_TAGS: Record<Tag, Tag[]> = {
    [Tag.MHCPlusPlus]: [],
    [Tag.Rpi]: [],
    [Tag.Baruch]: [],
    [Tag.Columbia]: [],
    [Tag.Patina]: [],
    [Tag.Sbu]: [],
    [Tag.Bmcc]: [],
    [Tag.Cornell]: [],
    [Tag.Hunter]: [Tag.Gwc, Tag.MHCPlusPlus],
    [Tag.Gwc]: [],
    [Tag.Nyu]: [],
    [Tag.Ccny]: [],
} as const;

Last updated: 02/15/2026


which allows us to define a complex relationship of parent tags to child tags. This Record is now currently being used in production to generate our Club Filters.

Filters

Last updated: 02/15/2026

ApiURL

You can view the implementation of ApiURL here.

ApiURL is a custom utility class designed to enforce type-safe requests when the frontend is sending/receiving data to/from the backend. It integrates directly with the generated schema.ts (read more about schema.ts here) file to ensure every fetch request — its method, parameters, body, and response — is validated at compile time.

Intent and Usage

ApiURL serves as the single entry point for building strongly-typed requests.
It provides the following core methods and behaviors:

  • ApiURL.create(path, options) — Static factory method to create ApiURL.that validates the provided path, method, and optionally path/query parameters.

    • path - Must be a valid endpoint path. > Note: URLs with dynamic paths are still supported.
  • .url — Accessor that returns the Web API URL object after substituting path and query parameters. This can be passed directly into fetch().

  • .method — Accessor returns the validated HTTP method (e.g., "GET", "POST") to use in fetch.
    Only allows valid methods defined in the backend OpenAPI schema.

  • .req(body) — Function that serializes and validates a request body (at compile-time) according to the backend’s expected type definition.
    Under the hood, .req calls JSON.stringify for you. As such, it returns a string for use as fetch's body.

  • .res(response) — Function that validates a JSON response against the expected type.
    Adds 0 runtime overhead - it’s purely a compile-time safety check.

    [!NOTE] T .res will always target an OK response. This is because Codebloom uses a custom ApiResponder type that will always return some fields back to the client. Read more about how it's implemented in the backend here.

POST request example

const { url, method, req, res } = ApiURL.create(
    "/api/admin/user/admin/toggle", // full autocomplete
    {
        method: "POST",
    },
);

const response = await fetch(url, {
    method,
    headers: { "Content-Type": "application/json" },
    body: req({ id: userId, toggleTo }), // full autocomplete
});

const json = res(await response.json());

return json; // `json` is fully typed

Dynamic path example

const { url, method, res } = ApiURL.create(
    "/api/leetcode/submission/{submissionId}", // full autocomplete
    {
        method: "GET",
        params: {
            // params has full autocomplete
            submissionId,
        },
    },
);
const response = await fetch(url, {
    method,
});

const json = res(await response.json());

return json; // `json` is fully typed