I am working on creating a REACT frontend along side my FastAPI backend, right now I am creating the skeleton UI for my Concept backend endpoints, this is the current .jsx page file:
```
import { Button, Drawer, theme } from "antd";
import React, { useState } from "react";

const Concepts = () => {
    const { token } = theme.useToken();
    const [open, setOpen] = useState(false);
//   const [concepts, setConcepts] = useState([]);


  const showDrawer = () => {
    setOpen(true);
  };
  const onClose = () => {
    setOpen(false);
  };

  const containerStyle = {
    position: "relative",
    height: '800px',
    padding: 48,
    overflow: "hidden",
    textAlign: "center",
    background: token.colorFillAlter,
    border: `1px solid ${token.colorBorderSecondary}`,
    borderRadius: token.borderRadiusLG,
  };

  // Render the complete component
  return (
    <div style={containerStyle}>
      <div>
        <Button type="primary" onClick={showDrawer}>
          Concepts
        </Button>
      </div>
      <Drawer
        title="Concepts"
        placement="left"
        closable={false}
        onClose={onClose}
        open={open}
        getContainer={false}
      >
        <p>Some concepts</p>
      </Drawer>
    </div>
  );
};

export default Concepts;
```

I also have an apiClient.js:
```
import axios from 'axios';

const API_BASE_URL = 'http://localhost:8000/api/v1'; // Update with your actual base API URL

const apiClient = axios.create({
  baseURL: API_BASE_URL,
  headers: {
    'Content-Type': 'application/json',
    // Include any other headers here, for example, Authorization headers for JWT.
  },
});

const fileClient = axios.create({
    baseURL: API_BASE_URL,
    headers: {
        'Content-Type': 'image',
    },
});

// CONCEPT ENDPOINTS
export const conceptCreate = async (name) => {
    return apiClient.post('/concept/create/', { name });
}

export const conceptGetById = async (concept_id) => {
    return apiClient.get(`/concept/${concept_id}`);
}

export const conceptsGetPaged = async (page=0, limit=10) => {
    return apiClient.get('/concept/', { params: { page, limit } });
}

export const conceptUpdateById = async (concept_id, name) => {
    return apiClient.put(`/concept/${concept_id}`, { name });
}

export const conceptDeleteById = async (concept_id) => {
    return apiClient.delete(`/concept/${concept_id}`);
}
```

I can also share the Concept database models, to give you more insight into the idea behind the Concept:
```
from sqlalchemy import Column, Index, Integer, Table, String, Text, ForeignKey, DateTime, func
from sqlalchemy import UniqueConstraint, CheckConstraint, MetaData
from sqlalchemy.orm import Mapped, Relationship # TODO: This
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship, declared_attr

from datetime import datetime
from typing import List, Optional
from .base import TimeStampedModel


class Concept(TimeStampedModel):
    """
    The Concept table represents an idea or thing, in general terms.

    Attributes:
        id (int): The unique identifier of the concept.
        name (str): The name of the concept.
        created_at (DateTime): The datetime this record was created.
        updated_at (DateTime): The datetime this record was last updated.
    """

    __tablename__ = "concept"

    # columns
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(100))

    # relationships
    concept_data_tables: Mapped[List["ConceptDataTable"]] = relationship()

    # methods
    def __repr__(self) -> str:
        return f"{self.__class__.__name__!r}(id={self.id!r}, name={self.name!r})" # TODO: Is this better?
        # return f"{self.__tablename__!r}(id={self.id!r}, name={self.name!r})"


"""
This links two concepts in a parent-child heirarchy.
Concepts:
Video Game
Franchise
Company
Half-Life 1
Half-Life
Valve

This table is perfect for grouping these concepts together, like so:
Video Game -> Half-Life 1
Franchise -> Half-Life
Company -> Valve

TODO: I suppose you COULD also do:
Valve -> Half-Life -> Half-Life 1
Rather than putting them as simply ConceptRelations.
The idea was for this to be a one-to-many relationship table though, rather than many-to-many.
So when displaying a concept, you can display it's "parent" concept if it has one.

Though I suppose when displaying concepts, you can pick and choose.

For example, on the Half-Life franchise page, it can specifically target all child concepts that have the parent "Video Game".
The key thing though, is you only group concepts below it if they are direct decendants.
You wouldn't do:
Concept[Half-Life] -> Concept[Character]
But you might do:
Concept[Half-Life] -> Concept[Gordon Freeman]

So when getting concept lists, you simply decide the ConceptGroups that concept falls under.
So for example, to get a list of all the Half-Life characters:
SELECT concept_id WHERE group_concept_id = Concept[Half-Life] AND group_concept_id = Concept[Character]

TODO: That won't work quite like that, you want to find where concept_id is found under both Concept[Half-Life] AND Concept[Character], I am not sure the SQL statement for achieving that.
"""
class ConceptGroup(TimeStampedModel):
    """
    This represents a hierarchy of grouping Concepts together, in a parent->child relationship.

    Attributes:
        group_concept_id: The parent concept.
        concept_id: The child concept.
        created_at (DateTime): The datetime this record was created.
        updated_at (DateTime): The datetime this record was last updated.
    """

    __tablename__ = "concept_group"

    # columns
    id: Mapped[int] = mapped_column(primary_key=True)
    group_concept_id = mapped_column(ForeignKey("concept.id"))
    concept_id = mapped_column(ForeignKey("concept.id"))

    __table_args__ = (
        # PrimaryKeyConstraint("group_concept_id", "concept_id", name="pk_concept_group_id"), # I don't think I can do this
        UniqueConstraint("group_concept_id", "concept_id"),
        CheckConstraint("group_concept_id != concept_id", name="ck_group_concept_id_concept_id_no_self_ref"),
    )

    # relationships
    group_concept: Mapped[Concept] = relationship(foreign_keys=[group_concept_id])
    concept: Mapped[Concept] = relationship(foreign_keys=[concept_id])

    # methods
    def __repr__(self) -> str:
        return f"{self.__tablename__!r}(id={self.id!r}, [{self.group_concept.name!r}->{self.concept.name}])"



"""
TODO: Do we still need this? If we do I will probably finish it, otherwise it can get removed.
This links two concepts that are related to each other.
We can link any two concepts, though this isn't a heirarchy, so avoid parent/child relationships here.
Example:
Heirarchy:
Concept[Video Game] -> Concept[Half-Life 1]
Concept[Franchise] -> Concept[Half-Life]

You would link Half-Life 1 to Half-Life as a Concept Relation, but you wouldn't link the Half-Life 1 concept directly to the Video Game concept, or link the Half-Life concept to the Franchise concept, that belongs in the ConceptGroup table that forms a heirarchy.
"""
# class ConceptRelation(TimeStampedModel):
#     __tablename__ = "concept_relation"

#     id = Column(Integer, primary_key=True)
#     concept1_id = Column(ForeignKey("concept.id"))
#     concept2_id = Column(ForeignKey("concept.id"))
#     created_at = mapped_column(DateTime(timezone=True), server_default=func.now())
#     updated_at = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())

#     __table_args__ = (
#         UniqueConstraint("concept1_id", "concept2_id"),
#     )


"""
VariableType is the key for all custom table metadata
"""
class VariableType(TimeStampedModel):
    """
    The VariableType model represents the types of variables used by other tables, which determines how to handle the value of
    variables in code.

    Attributes:
        id (int): The unique identifier of the variable type.
        name (str): The name of the variable type.
        created_at (DateTime): The datetime this record was created.
        updated_at (DateTime): The datetime this record was last updated.
    """
    __tablename__ = "variable_type"

    # columns
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30), unique=True)

    # methods
    def __repr__(self):
        return f"{self.__tablename__!r}(id={self.id!r}, name={self.name!r})"


class HashType(TimeStampedModel):
    """
    The HashType model represents the types of hashes used by the Hash table to help separate different types of hashes.

    Attributes:
        id (int): The unique identifier of the hash type.
        name (str): The name of the hash type.
        created_at (DateTime): The datetime this record was created.
        updated_at (DateTime): The datetime this record was last updated.
    """

    __tablename__ = "hash_type"

    # columns
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30), unique=True)

    # methods
    def __repr__(self):
        return f"{self.__tablename__!r}(id={self.id!r}, name={self.name!r})"


# TODO: In regard to fingerprinting, use a whitelist system for file types to fingerprint
# This way we can slowly build up types to fingerprint, and mark types as volatile or not(.txt files are likely all volatile for example)

class Fingerprint(TimeStampedModel):
    """
    The Fingerprint model represents a data fingerprint, which contains a SHA512 hash of the data.

    Attributes:
        id (int): The unique identifier of the fingerprint.
        fingerprint (str): The SHA512 hash of the data.
        size (int): The size of the data in bytes.
        created_at (DateTime): The datetime this record was created.
        updated_at (DateTime): The datetime this record was last updated.
        files (List[File]): One-to-many relationship with File models representing the physical files associated with the fingerprint.
        hashes (List[Hash]): One-to-many relationship with Hash models representing the different types of hashes associated with the fingerprint.
    """

    __tablename__ = "fingerprint"

    # columns
    id: Mapped[int] = mapped_column(primary_key=True)
    hash: Mapped[str] = mapped_column(String(128))
    size: Mapped[int]

    __table_args__ = (
        # Index('idx_fingerprint_hash_size', Column('hash'), Column('size'), unique=True), # Don't think I want to combine this
        # PrimaryKeyConstraint('hash', 'size', name='pk_fingerprint_id'), # Don't think I can do this
        UniqueConstraint('hash', 'size'),
    )

    # relationships
    hashes: Mapped[List["Hash"]] = relationship()

    # methods
    def __repr__(self):
        return f"{self.__tablename__!r}(id={self.id!r}, fingerprint=[{self.hash!r}, {self.size!r}])"



class Hash(TimeStampedModel):
    """
    The Hash model represents the different types of hashes associated with a fingerprint.

    Attributes:
        id (int): The unique identifier of the hash.
        hash (str): The value of the hash.
        hash_type_id (int): The foreign key reference to the HashType model to store the type of the hash.
        fingerprint_id (int): The foreign key reference to the Fingerprint model to represent the relationship between the hash and its parent fingerprint.
        created_at (DateTime): The datetime this record was created.
        updated_at (DateTime): The datetime this record was last updated.
        hash_type (HashType): One-to-one relationship with the HashType model.
        fingerprint (Fingerprint): One-to-one relationship with the Fingerprint model.
    """

    __tablename__ = "hash"

    # columns
    # TODO: This SHOULD work, to have fingerprint_id as the primary key
    # id = Column(Integer, primary_key=True)
    fingerprint_id: Mapped[int] = mapped_column(ForeignKey('fingerprint.id'), primary_key=True)
    hash: Mapped[str] = mapped_column(String(256))
    hash_type_id = mapped_column(ForeignKey('hash_type.id'))

    __table_args__ = (
        UniqueConstraint("fingerprint_id", "hash_type_id"),
        Index("idx_fp_hash_type_id", fingerprint_id, hash_type_id)
    )

    # relationships
    fingerprint: Mapped[Fingerprint] = relationship(back_populates="hashes", foreign_keys=[fingerprint_id])
    hash_type: Mapped[HashType] = relationship(foreign_keys=[hash_type_id])

    # methods
    def __repr__(self):
        return f"{self.__tablename__!r}(id={self.id!r}, hash=[{self.hash_type.name!r}={self.hash!r}])"



"""
The basic idea is the fields table contains a list of columns that are in the data table.
The data table contains whatever data we want to link to this concept
And instead of this table having it's own id, we link directly to the concept id.

TODO: We need a uniform way for different Concepts to link to different ConceptData.

For example, I want an Image concept, this concept has a data table that stores information on images; widthXheight, hashes, etc.
But I want the Gelbooru concepts data table to be able to reference an Image ID directly, without having to duplicate data, so I can append extra information, such as the gelbooru post ID, tags etc.
So we probably want a ForeignKey VariableType.
"""
class ConceptDataTable(TimeStampedModel):
    __tablename__ = "concept_data_table"

    # columns
    # id = Column(Integer, primary_key=True)
    concept_id: Mapped[int] = mapped_column(ForeignKey('concept.id'), primary_key=True)
    data_table_name: Mapped[str] = mapped_column(unique=True)
    fields_table_name: Mapped[str] = mapped_column(unique=True)

    # relationships
    concept: Mapped[Concept] = relationship(foreign_keys=[concept_id], back_populates='concept_data_tables')

    # methods
    def __repr__(self):
        return f"{self.__tablename__!r}(concept={self.concept.name!r}, data_table_name={self.data_table_name!r}, fields_table_name={self.fields_table_name!r})"

















"""
This does not yet contain the Fingerprints.

I feel like the Data Fingerprint can itself be a Concept.
It can have a Fingerprint data table containing all the fingerprints.
All it needs is the fingerprint hash itself, and the data size.

I do however also want support for all types of hashing.
So, Concept[Hash]
This concept groups all hash algorithms.
Concept[Hash]
 \-> Concept[AUTOV1]
 \-> Concept[AUTOV2]
 \-> Concept[MD5]
 \-> Concept[SHA256]
 \-> Concept[SHA512]
 \-> Concept[CRC32]
 \-> Concept[BLAKE3]

Though, how to link all the hashes to the same data...

Idea 1:
Instead of having VariableType TEXT, have VariableType HASH_SHA256, which gets stored as a TEXT entry in the DB, but the.

No, that is too specific and I wouldn't be using those VariableTypes outside of the data table, making them useless to be a VariableType.

Idea 2:
A data table, indexed by Fingerprint(SHA512 hash + size)
I can then have individual hash concepts, with their data tables link to the Fingerprint:
id, fingerprint, hash

Idea 3:
Do what I did for another project, have a HashType table, then have a hash table:
FK fingerprint_id, hash, hashtype_id

modified to be indexed by the fingerprint_id as a foreignkey, rather than have its own id
The fingerprint would then have it's own table:
id, hash, size

Then with the ConceptDataTable idea, I can have a Fingerprint as a VariableType, which becomes a foreign key into the fingerprint table

NOTE: Additional idea, allow setting the ConceptDataTables id type, maybe have the VariableType of PrimaryKey to indicate a regular PK index, but a ForeignKey to have it be indexed by a different tables key.
"""

"""
TABLE -> VariableType
TABLE -> Concept
ROW[Concept] -> Name='Gelbooru'
ROW[Concept] -> Name='Booru'
TABLE -> ConceptGroup           # These are basically like category grouping, this is used to tie all Video Games to the Video Game concept, or all Boorus to the Booru concept, so to get a list of all video games I can check the Concept Group, also links a company like Valve to be under the Corporate Entity concept.
# Each Concept can only be in one Concept Group, so the Concept must be unique, but the GroupConcept can have multiple, a ONE-TO-MANY database relationship.
TABLE -> ConceptRelated         # These are to link related concepts together, but rather than a parent-child relationship, even if some concepts might be "above" another, this is to simply link related concepts together, like linking "Half-Life 1" to "Valve" company, and also linking it to "Half-Life" franchise, you could also go beyond, linking it to the "First Person Shooter" genre("Genre" being another concept that contains "First Person Shooter" in it's group)
ROW[ConceptGroup] -> GroupConcept='Booru', Concept='Gelbooru'       #Groups Gelbooru under the Booru concept
ROW[ConceptRelated] -> Concept1='Gelbooru', Concept2='Image'        #Without a better example, could do this

These must be freely changable, and bulk movable, in case a new concept is added and other concepts need to be re-organized.
Concepts also do not need a group, or a related concept.

I would like to do some auto-generated variable tables, we need some structured way to manage this within the UI, rather than having to code new python modules manually.

As an example, on Gelbooru, there is images, or "posts", breaking this down into yet another Concept that only has this very specific use seems impractical, instead we bring it back to the more generic "Image" concept.
For each post on Gelbooru, there is a bunch of information, the Image, ID, score, md5 hash, tags, created date, rating, source, notes, comments, has_children etc.
To collect all this data, I would like it to be specific to Gelbooru, rather than collecting it generically, I mean, certain things can be more generalized, like the md5 hash, the width, the height, and the image itself.
So what about the other data?

Create a specific table for it!
TABLE -> ImageGelbooruVariables
FIELDS -> VariableTypeId, ImageId, Name, Value

How do we manage this table? Another table!
TABLE -> ConceptLinkingVariables
FIELDS -> Concept1Id, Concept2Id, TableName

Hmm, but how to link Concepts with custom tables, like the Image table? Maybe Adapt the above with an extra field?
TABLE -> ConceptLinkedTable
FIELDS -> ConceptId, TableName, VariableTableName
ROW[ConceptLinkedTable] -> ImageConcept, Image, ImageVariables

The VariableTable is a table that contains the variable fields within the Table
TABLE -> ImageVariables
FIELDS -> Id, VariableName(The name of the field), VariableTypeId(The datatype of the field)

(The VariableTypeId would be an Id into the VariableType table, but for clarity I will use a string here):
ROW[ImageVariables] -> 0, 'Id', 'INTEGER'
ROW[ImageVariables] -> 1, 'ImagePath', 'TEXT'
ROW[ImageVariables] -> 2, 'Width', 'INTEGER'
ROW[ImageVariables] -> 3, 'Height', 'INTEGER'

So what this means is we make a table like this:
TABLE -> Image
FIELDS -> Id(INTEGER), ImagePath(TEXT), Width(INTEGER), Height(INTEGER)
"""
```
There is a couple of large comments in there, I apologise for sending so much at once.
Right now I am just building out the REACT UI side, to start with, I want to pull in a list of all the concepts, to go in the antd Drawer element as a list I can select from, I don't have any calls in place for the ConceptGroup yet, but I also want to be able to manage this list in some way, but since a Concept might be part of multiple ConceptGroups, I cannot just group them in the list, instead I want to have a list of Concepts, with a UI element to be able to put a Concept into a group, I also want to allow creating new Concepts and ConceptGroups.
Remember that a ConceptGroup merely creates a Parent Concept -> Children Concept, one-to-many grouping, so to create a ConceptGroup, we need to attach another concept as a parent to the current Concept.