Create entites for each table to setup schema

This commit is contained in:
emmalynf 2024-03-22 23:15:38 -04:00
parent 035ab5fadb
commit b8903adeb8
42 changed files with 5499 additions and 5249 deletions

View File

@ -1,72 +1,72 @@
FROM ubuntu:22.04 FROM ubuntu:22.04
# Setup workspace directory # Setup workspace directory
RUN mkdir /workspace RUN mkdir /workspace
WORKDIR /workspace WORKDIR /workspace
# Install useful system utilities # Install useful system utilities
ENV TZ=America/New_York ENV TZ=America/New_York
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \ RUN apt-get update \
&& apt-get install --yes \ && apt-get install --yes \
apt-transport-https \ apt-transport-https \
ca-certificates \ ca-certificates \
curl \ curl \
debian-keyring \ debian-keyring \
debian-archive-keyring \ debian-archive-keyring \
git \ git \
gnupg \ gnupg \
locales \ locales \
postgresql-client \ postgresql-client \
software-properties-common \ software-properties-common \
sudo \ sudo \
tzdata \ tzdata \
wget \ wget \
zsh \ zsh \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Install Python 3.11 # Install Python 3.11
RUN add-apt-repository ppa:deadsnakes/ppa \ RUN add-apt-repository ppa:deadsnakes/ppa \
&& apt update \ && apt update \
&& apt install --yes \ && apt install --yes \
python3.11 \ python3.11 \
python3-pip \ python3-pip \
libpq-dev \ libpq-dev \
python3.11-dev \ python3.11-dev \
&& rm -rf /var/lib/apt/lists* \ && rm -rf /var/lib/apt/lists* \
&& unlink /usr/bin/python3 \ && unlink /usr/bin/python3 \
&& ln -s /usr/bin/python3.11 /usr/bin/python3 && ln -s /usr/bin/python3.11 /usr/bin/python3
# Use a non-root user per https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user # Use a non-root user per https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user
ARG USERNAME=vscode ARG USERNAME=vscode
ARG USER_UID=1000 ARG USER_UID=1000
ARG USER_GID=$USER_UID ARG USER_GID=$USER_UID
# Add non-root user and add to sudoers # Add non-root user and add to sudoers
RUN groupadd --gid $USER_GID $USERNAME \ RUN groupadd --gid $USER_GID $USERNAME \
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /usr/bin/zsh \ && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME -s /usr/bin/zsh \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME && chmod 0440 /etc/sudoers.d/$USERNAME
# Set code to default git commit editor # Set code to default git commit editor
RUN git config --system core.editor "code --wait" RUN git config --system core.editor "code --wait"
# Set Safe Directory # Set Safe Directory
RUN git config --system safe.directory '/workspace' RUN git config --system safe.directory '/workspace'
# Configure zsh # Configure zsh
USER $USERNAME USER $USERNAME
ENV HOME /home/$USERNAME ENV HOME /home/$USERNAME
# Add zsh theme with niceties # Add zsh theme with niceties
RUN curl https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh | bash - \ RUN curl https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh | bash - \
&& sed -i 's/robbyrussell/kennethreitz/g' ~/.zshrc \ && sed -i 's/robbyrussell/kennethreitz/g' ~/.zshrc \
&& echo 'source <(ng completion script)' >>~/.zshrc \ && echo 'source <(ng completion script)' >>~/.zshrc \
&& echo 'export PATH=$PATH:$HOME/.local/bin' >>~/.zshrc && echo 'export PATH=$PATH:$HOME/.local/bin' >>~/.zshrc
# Set Locale for Functional Autocompletion in zsh # Set Locale for Functional Autocompletion in zsh
RUN sudo locale-gen en_US.UTF-8 RUN sudo locale-gen en_US.UTF-8
# Install Database Dependencies # Install Database Dependencies
COPY backend/requirements.txt /workspace/backend/requirements.txt COPY backend/requirements.txt /workspace/backend/requirements.txt
WORKDIR /workspace/backend WORKDIR /workspace/backend
RUN python3 -m pip install -r requirements.txt RUN python3 -m pip install -r requirements.txt

View File

@ -1,47 +1,47 @@
{ {
"dockerComposeFile": "docker-compose.yml", "dockerComposeFile": "docker-compose.yml",
"workspaceFolder": "/workspace", "workspaceFolder": "/workspace",
"service": "httpd", "service": "httpd",
"remoteUser": "vscode", "remoteUser": "vscode",
"forwardPorts": [ "forwardPorts": [
5432, 5432,
5050 5050
], ],
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"ecmel.vscode-html-css", "ecmel.vscode-html-css",
"ms-vscode.vscode-typescript-next", "ms-vscode.vscode-typescript-next",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"bradlc.vscode-tailwindcss", "bradlc.vscode-tailwindcss",
"vscode-icons-team.vscode-icons", "vscode-icons-team.vscode-icons",
"tamasfe.even-better-toml", "tamasfe.even-better-toml",
"ckolkman.vscode-postgres", "ckolkman.vscode-postgres",
"ms-python.python", "ms-python.python",
"ms-python.vscode-pylance", "ms-python.vscode-pylance",
"ms-python.black-formatter", "ms-python.black-formatter",
"gruntfuggly.todo-tree", "gruntfuggly.todo-tree",
"ms-azuretools.vscode-docker" "ms-azuretools.vscode-docker"
], ],
"settings": { "settings": {
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnSaveMode": "file", "editor.formatOnSaveMode": "file",
"[python]": { "[python]": {
"editor.defaultFormatter": "ms-python.black-formatter" "editor.defaultFormatter": "ms-python.black-formatter"
}, },
"python.analysis.extraPaths": [ "python.analysis.extraPaths": [
"/backend/" "/backend/"
], ],
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.analysis.diagnosticSeverityOverrides": { "python.analysis.diagnosticSeverityOverrides": {
"reportMissingParameterType": "error", "reportMissingParameterType": "error",
"reportGeneralTypeIssues": "error", "reportGeneralTypeIssues": "error",
"reportDeprecated": "error", "reportDeprecated": "error",
"reportImportCycles": "error" "reportImportCycles": "error"
} }
} }
} }
} }
} }

View File

@ -1,35 +1,35 @@
version: "3" version: "3"
services: services:
httpd: httpd:
build: build:
context: .. context: ..
dockerfile: .devcontainer/Dockerfile dockerfile: .devcontainer/Dockerfile
volumes: volumes:
- ..:/workspace - ..:/workspace
command: /bin/sh -c "while sleep 1000; do :; done" command: /bin/sh -c "while sleep 1000; do :; done"
environment: environment:
- windir # Defined on Windows but not on other platforms - windir # Defined on Windows but not on other platforms
db: db:
image: "postgres:latest" image: "postgres:latest"
ports: ports:
- "5432:5432" - "5432:5432"
env_file: env_file:
- ../backend/.env - ../backend/.env
volumes: volumes:
- compass-center-postgres:/var/lib/postgresql/data - compass-center-postgres:/var/lib/postgresql/data
# - ./backend/init.sql:/docker-entrypoint-initdb.d/init.sql:ro # - ./backend/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
pgadmin: pgadmin:
image: dpage/pgadmin4:latest image: dpage/pgadmin4:latest
environment: environment:
PGADMIN_DEFAULT_EMAIL: admin@example.com PGADMIN_DEFAULT_EMAIL: admin@example.com
PGADMIN_DEFAULT_PASSWORD: admin PGADMIN_DEFAULT_PASSWORD: admin
ports: ports:
- "5050:80" - "5050:80"
depends_on: depends_on:
- db - db
volumes: volumes:
compass-center-postgres: compass-center-postgres:

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
/backend/.env /backend/.env
__pycache__ __pycache__

210
README.md
View File

@ -1,105 +1,105 @@
# 🧭 Compass Center's Internal Resource Management App # 🧭 Compass Center's Internal Resource Management App
## 🛠 Technologies ## 🛠 Technologies
- Next.js - Next.js
- TailwindCSS - TailwindCSS
- TypeScript - TypeScript
- Supabase - Supabase
## 📁 File Setup ## 📁 File Setup
``` ```
\compass \compass
\components // Components organized in folders related to specific pages \components // Components organized in folders related to specific pages
\pages // Store all pages here \pages // Store all pages here
\api // API routes \api // API routes
\public // Local assets (minimize usage) \public // Local assets (minimize usage)
\utils // Constants, Routes, Classes, Dummy Data \utils // Constants, Routes, Classes, Dummy Data
\styles // CSS files \styles // CSS files
``` ```
## 🚀 To Start ## 🚀 To Start
Follow these steps to set up your local environment: Follow these steps to set up your local environment:
``` ```
\\ Clone this repository \\ Clone this repository
git clone https://github.com/cssgunc/compass.git git clone https://github.com/cssgunc/compass.git
\\ Go into main folder \\ Go into main folder
cd compass cd compass
\\ Install dependencies \\ Install dependencies
npm install npm install
\\ Run local environment \\ Run local environment
npm run dev npm run dev
``` ```
Also add following variables inside of a .env file inside of the backend directory Also add following variables inside of a .env file inside of the backend directory
``` ```
\\ .env file contents \\ .env file contents
POSTGRES_DATABASE=compass POSTGRES_DATABASE=compass
POSTGRES_USER=postgres POSTGRES_USER=postgres
POSTGRES_PASSWORD=admin POSTGRES_PASSWORD=admin
POSTGRES_HOST=db POSTGRES_HOST=db
POSTGRES_PORT=5432 POSTGRES_PORT=5432
HOST=localhost HOST=localhost
``` ```
## Backend Starter ## Backend Starter
- Please open the VS Code Command Palette - Please open the VS Code Command Palette
- Run the command **Dev Containers: Rebuild and Reopen in Container** - Run the command **Dev Containers: Rebuild and Reopen in Container**
- This should open the dev container with the same file directory mounted so any changes in the dev container will be seen in the local repo - This should open the dev container with the same file directory mounted so any changes in the dev container will be seen in the local repo
### In Dev Container ### In Dev Container
Run this to reset the database and populate it with the approprate tables that reflect the entities folder Run this to reset the database and populate it with the approprate tables that reflect the entities folder
``` ```
python3 -m backend.script.reset_demo python3 -m backend.script.reset_demo
``` ```
### Possible Dev Container Errors ### Possible Dev Container Errors
- Sometimes the ports allocated to our services will be allocated (5432 for Postgres and 5050 for PgAdmin4) - Sometimes the ports allocated to our services will be allocated (5432 for Postgres and 5050 for PgAdmin4)
- Run **docker stop** to stop all containers - Run **docker stop** to stop all containers
- If that does not work using **sudo lsof -i :[PORT_NUMBER]** to find the process running on the needed ports and idenitfy the PID - If that does not work using **sudo lsof -i :[PORT_NUMBER]** to find the process running on the needed ports and idenitfy the PID
- Run **sudo kill [PID]** - Run **sudo kill [PID]**
- If you are on Windows please consult ChatGPT or set up WSL (will be very useful in the future) - If you are on Windows please consult ChatGPT or set up WSL (will be very useful in the future)
### Accesing pgAdmin 4 ### Accesing pgAdmin 4
- First go to http://localhost:5050/ on your browser - First go to http://localhost:5050/ on your browser
- Log in using the credentials admin@example.com and admin - Log in using the credentials admin@example.com and admin
- Click **Add New Server** - Click **Add New Server**
- Fill in the name field with Compass (can be anything) - Fill in the name field with Compass (can be anything)
- Click **Connection** tab and fill in the following: - Click **Connection** tab and fill in the following:
- Host name/address: db - Host name/address: db
- Maintence database: compass - Maintence database: compass
- Username: postgres - Username: postgres
- Password: admin - Password: admin
- Click **Save** at the bottom to add connection - Click **Save** at the bottom to add connection
- Click **Server** dropdown on the left and click through items inside the **Compass** server - Click **Server** dropdown on the left and click through items inside the **Compass** server
### Testing Backend Code ### Testing Backend Code
- Write tests for any service you create and any function in those services - Write tests for any service you create and any function in those services
- Make sure to add docstrings detailing what the file is doing and what each test is doing - Make sure to add docstrings detailing what the file is doing and what each test is doing
- Name all test functions with test\_[testContent] (Must be prefixed with test to be recognized by pytest) - Name all test functions with test\_[testContent] (Must be prefixed with test to be recognized by pytest)
- Utitlize dependency injection for commonly used services - Utitlize dependency injection for commonly used services
``` ```
\\ Run all tests by being in the backend directory \\ Run all tests by being in the backend directory
pytest pytest
\\ Run specific tests by passing in file as a parameter \\ Run specific tests by passing in file as a parameter
\\ Passing the -s allows us to see any print statements or debugging statements in the console \\ Passing the -s allows us to see any print statements or debugging statements in the console
pytest -s --rootdir=/workspace [testFilePath]::[testFunctionSignature] pytest -s --rootdir=/workspace [testFilePath]::[testFunctionSignature]
``` ```
## 💡 Dev Notes ## 💡 Dev Notes
- For each task, create a branch in the format '[your name]-[ticket number]-[task description]' - For each task, create a branch in the format '[your name]-[ticket number]-[task description]'
- Only commit your work to that branch and then make a git request to '/main' - Only commit your work to that branch and then make a git request to '/main'
- When creating new files in the backend and code is in python make sure to add a docstring for the file and any function you create ("""[content]"""") - When creating new files in the backend and code is in python make sure to add a docstring for the file and any function you create ("""[content]"""")

View File

@ -1,29 +1,29 @@
"""SQLAlchemy DB Engine and Session niceties for FastAPI dependency injection.""" """SQLAlchemy DB Engine and Session niceties for FastAPI dependency injection."""
import sqlalchemy import sqlalchemy
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from .env import getenv from .env import getenv
def _engine_str(database: str = getenv("POSTGRES_DATABASE")) -> str: def _engine_str(database: str = getenv("POSTGRES_DATABASE")) -> str:
"""Helper function for reading settings from environment variables to produce connection string.""" """Helper function for reading settings from environment variables to produce connection string."""
dialect = "postgresql+psycopg2" dialect = "postgresql+psycopg2"
user = getenv("POSTGRES_USER") user = getenv("POSTGRES_USER")
password = getenv("POSTGRES_PASSWORD") password = getenv("POSTGRES_PASSWORD")
host = getenv("POSTGRES_HOST") host = getenv("POSTGRES_HOST")
port = getenv("POSTGRES_PORT") port = getenv("POSTGRES_PORT")
return f"{dialect}://{user}:{password}@{host}:{port}/{database}" return f"{dialect}://{user}:{password}@{host}:{port}/{database}"
engine = sqlalchemy.create_engine(_engine_str(), echo=True) engine = sqlalchemy.create_engine(_engine_str(), echo=True)
"""Application-level SQLAlchemy database engine.""" """Application-level SQLAlchemy database engine."""
def db_session(): def db_session():
"""Generator function offering dependency injection of SQLAlchemy Sessions.""" """Generator function offering dependency injection of SQLAlchemy Sessions."""
print("ran") print("ran")
session = Session(engine) session = Session(engine)
try: try:
yield session yield session
finally: finally:
session.close() session.close()

View File

@ -1,2 +1,2 @@
from .entity_base import EntityBase from .entity_base import EntityBase
from .sample_entity import SampleEntity from .sample_entity import SampleEntity

View File

@ -1,12 +1,12 @@
"""Abstract superclass of all entities in the application. """Abstract superclass of all entities in the application.
There is no reason to instantiate this class directly. Instead, look toward the child classes. There is no reason to instantiate this class directly. Instead, look toward the child classes.
Additionally, import from the top-level entities file which indexes all entity implementations. Additionally, import from the top-level entities file which indexes all entity implementations.
""" """
from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import DeclarativeBase
class EntityBase(DeclarativeBase): class EntityBase(DeclarativeBase):
pass pass

View File

@ -0,0 +1,38 @@
""" Defines the table for storing resources """
# Import our mapped SQL types from SQLAlchemy
from sqlalchemy import Integer, String, DateTime, ARRAY
# Import mapping capabilities from the SQLAlchemy ORM
from sqlalchemy.orm import Mapped, mapped_column, relationship
# Import the EntityBase that we are extending
from .entity_base import EntityBase
# Import datetime for created_at type
from datetime import datetime
# Import enums for Program
import enum
from sqlalchemy import Enum
class ProgramEnum(enum.Enum):
"""Determine program for Resource """
DOMESTIC = 'DOMESTIC'
ECONOMIC = 'ECONOMIC'
COMMUNITY = 'COMMUNITY'
class ResourceEntity(EntityBase):
#set table name
__tablename__ = "resource"
#set fields
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
name: Mapped[str] = mapped_column(String(32), nullable=False)
summary: Mapped[str] = mapped_column(String(100), nullable=False)
link: Mapped[str] = mapped_column(String, nullable=False)
program: Mapped[ProgramEnum] = mapped_column(Enum(ProgramEnum), nullable=False)
#relationships
resourceTags: Mapped[list["ResourceTagEntity"]] = relationship(back_populates="resource", cascade="all,delete")

View File

@ -0,0 +1,32 @@
""" Defines the table for resource tags """
# Import our mapped SQL types from SQLAlchemy
from sqlalchemy import ForeignKey, Integer, String, DateTime, ARRAY
# Import mapping capabilities from the SQLAlchemy ORM
from sqlalchemy.orm import Mapped, mapped_column, relationship
# Import the EntityBase that we are extending
from .entity_base import EntityBase
# Import datetime for created_at type
from datetime import datetime
# Import enums for Role and Program
import enum
from sqlalchemy import Enum
class ResourceTagEntity(EntityBase):
# set table name to user in the database
__tablename__ = "resourceTag"
# set fields or 'columns' for the user table
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
resourceId: Mapped[int] = mapped_column(ForeignKey("event.id"), primary_key=True)
tagId: Mapped[int] = mapped_column(ForeignKey("user.pid"), primary_key=True)
# relationships
resource: Mapped["ResourceEntity"] = relationship(back_populates="resourceTags")
tag: Mapped["TagEntity"] = relationship(back_populates="resourceTags")

View File

@ -1,12 +1,12 @@
from sqlalchemy import create_engine, Column, Integer, String from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship
from .entity_base import EntityBase from .entity_base import EntityBase
class SampleEntity(EntityBase): class SampleEntity(EntityBase):
__tablename__ = "persons" __tablename__ = "persons"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
name: Mapped[str] = mapped_column(String, nullable=False) name: Mapped[str] = mapped_column(String, nullable=False)
age: Mapped[int] = mapped_column(Integer) age: Mapped[int] = mapped_column(Integer)
email: Mapped[str] = mapped_column(String, unique=True, nullable=False) email: Mapped[str] = mapped_column(String, unique=True, nullable=False)

View File

@ -0,0 +1,41 @@
""" Defines the table for storing services """
# Import our mapped SQL types from SQLAlchemy
from sqlalchemy import Integer, String, DateTime, ARRAY
# Import mapping capabilities from the SQLAlchemy ORM
from sqlalchemy.orm import Mapped, mapped_column, relationship
# Import the EntityBase that we are extending
from .entity_base import EntityBase
# Import datetime for created_at type
from datetime import datetime
# Import enums for Program
import enum
from sqlalchemy import Enum
class ProgramEnum(enum.Enum):
"""Determine program for Service"""
DOMESTIC = "DOMESTIC"
ECONOMIC = "ECONOMIC"
COMMUNITY = "COMMUNITY"
class ResourceEntity(EntityBase):
#set table name
__tablename__ = "service"
#set fields
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
name: Mapped[str] = mapped_column(String(32), nullable=False)
summary: Mapped[str] = mapped_column(String(100), nullable=False)
requirements: Mapped[list[str]] = mapped_column(ARRAY(String))
program: Mapped[ProgramEnum] = mapped_column(Enum(ProgramEnum), nullable=False)
#relationships
resourceTags: Mapped[list["ServiceTagEntity"]] = relationship(back_populates="service", cascade="all,delete")

View File

@ -0,0 +1,25 @@
""" Defines the table for service tags """
# Import our mapped SQL types from SQLAlchemy
from sqlalchemy import ForeignKey, Integer
# Import mapping capabilities from the SQLAlchemy ORM
from sqlalchemy.orm import Mapped, mapped_column, relationship
# Import the EntityBase that we are extending
from .entity_base import EntityBase
class ResourceTagEntity(EntityBase):
# set table name to user in the database
__tablename__ = "serviceTag"
# set fields or 'columns' for the user table
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
sericeId: Mapped[int] = mapped_column(ForeignKey("event.id"), primary_key=True)
tagId: Mapped[int] = mapped_column(ForeignKey("user.pid"), primary_key=True)
# relationships
service: Mapped["ServiceEntity"] = relationship(back_populates="resourceTags")
tag: Mapped["TagEntity"] = relationship(back_populates="resourceTags")

View File

@ -0,0 +1,28 @@
""" Defines the table for storing tags """
# Import our mapped SQL types from SQLAlchemy
from sqlalchemy import Integer, String, DateTime
# Import mapping capabilities from the SQLAlchemy ORM
from sqlalchemy.orm import Mapped, mapped_column, relationship
# Import the EntityBase that we are extending
from .entity_base import EntityBase
# Import datetime for created_at type
from datetime import datetime
class TagEntity(EntityBase):
#set table name
__tablename__ = "tag"
#set fields
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
content: Mapped[str] = mapped_column(String(100), nullable=False)
#relationships
resourceTags: Mapped[list["ResourceTagEntity"]] = relationship(back_populates="tag", cascade="all,delete")
serviceTags: Mapped[list["ServiceTagEntity"]] = relationship(back_populates="tag", cascade="all,delete")

View File

@ -0,0 +1,43 @@
""" Defines the table for storing users """
# Import our mapped SQL types from SQLAlchemy
from sqlalchemy import Integer, String, DateTime, ARRAY
# Import mapping capabilities from the SQLAlchemy ORM
from sqlalchemy.orm import Mapped, mapped_column
# Import the EntityBase that we are extending
from .entity_base import EntityBase
# Import datetime for created_at type
from datetime import datetime
# Import enums for Role and Program
import enum
from sqlalchemy import Enum
class RoleEnum(enum.Enum):
""" Determine role for User """
ADMIN = 'ADMIN'
EMPLOYEE = 'EMPLOYEE'
VOLUNTEER = 'VOLUNTEER'
class ProgramEnum(enum.Enum):
"""Determine program for User """
DOMESTIC = 'DOMESTIC'
ECONOMIC = 'ECONOMIC'
COMMUNITY = 'COMMUNITY'
class UserEntity(EntityBase):
"""Serves as the databse model for User table"""
# set table name to user in the database
__tablename__ = "user"
# set fields or 'columns' for the user table
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
username: Mapped[str] = mapped_column(String(32), nullable=False, default="", unique=True)
role: Mapped[RoleEnum] = mapped_column(Enum(RoleEnum), nullable=False)
email: Mapped[str] = mapped_column(String(50), nullable=False, unique=True)
program: Mapped[list[ProgramEnum]] = mapped_column(ARRAY(Enum(ProgramEnum)), nullable=False)
experience: Mapped[int] = mapped_column(Integer, nullable=False)
group: Mapped[str] = mapped_column(String(50))

View File

@ -1,21 +1,21 @@
"""Load environment variables from a .env file or the process' environment.""" """Load environment variables from a .env file or the process' environment."""
import os import os
import dotenv import dotenv
# Load envirnment variables from .env file upon module start. # Load envirnment variables from .env file upon module start.
dotenv.load_dotenv(f"{os.path.dirname(__file__)}/.env", verbose=True) dotenv.load_dotenv(f"{os.path.dirname(__file__)}/.env", verbose=True)
def getenv(variable: str) -> str: def getenv(variable: str) -> str:
"""Get value of environment variable or raise an error if undefined. """Get value of environment variable or raise an error if undefined.
Unlike `os.getenv`, our application expects all environment variables it needs to be defined Unlike `os.getenv`, our application expects all environment variables it needs to be defined
and we intentionally fast error out with a diagnostic message to avoid scenarios of running and we intentionally fast error out with a diagnostic message to avoid scenarios of running
the application when expected environment variables are not set. the application when expected environment variables are not set.
""" """
value = os.getenv(variable) value = os.getenv(variable)
if value is not None: if value is not None:
return value return value
else: else:
raise NameError(f"Error: {variable} Environment Variable not Defined") raise NameError(f"Error: {variable} Environment Variable not Defined")

View File

@ -1,6 +1,6 @@
fastapi[all] >=0.100.0, <0.101.0 fastapi[all] >=0.100.0, <0.101.0
sqlalchemy >=2.0.4, <2.1.0 sqlalchemy >=2.0.4, <2.1.0
psycopg2 >=2.9.5, <2.10.0 psycopg2 >=2.9.5, <2.10.0
alembic >=1.10.2, <1.11.0 alembic >=1.10.2, <1.11.0
pytest >=7.2.1, <7.3.0 pytest >=7.2.1, <7.3.0
python-dotenv >=1.0.0, <1.1.0 python-dotenv >=1.0.0, <1.1.0

View File

@ -1,14 +1,14 @@
from sqlalchemy import text, create_engine from sqlalchemy import text, create_engine
from ..database import engine, _engine_str from ..database import engine, _engine_str
from ..env import getenv from ..env import getenv
engine = create_engine(_engine_str(database=""), echo=True) engine = create_engine(_engine_str(database=""), echo=True)
"""Application-level SQLAlchemy database engine.""" """Application-level SQLAlchemy database engine."""
with engine.connect() as connection: with engine.connect() as connection:
connection.execute( connection.execute(
text("COMMIT") text("COMMIT")
) )
database = getenv("POSTGRES_DATABASE") database = getenv("POSTGRES_DATABASE")
stmt = text(f"CREATE DATABASE {database}") stmt = text(f"CREATE DATABASE {database}")
connection.execute(stmt) connection.execute(stmt)

View File

@ -1,14 +1,14 @@
from sqlalchemy import text, create_engine from sqlalchemy import text, create_engine
from ..database import engine, _engine_str from ..database import engine, _engine_str
from ..env import getenv from ..env import getenv
engine = create_engine(_engine_str(database=""), echo=True) engine = create_engine(_engine_str(database=""), echo=True)
"""Application-level SQLAlchemy database engine.""" """Application-level SQLAlchemy database engine."""
with engine.connect() as connection: with engine.connect() as connection:
connection.execute( connection.execute(
text("COMMIT") text("COMMIT")
) )
database = getenv("POSTGRES_DATABASE") database = getenv("POSTGRES_DATABASE")
stmt = text(f"DROP DATABASE IF EXISTS {database}") stmt = text(f"DROP DATABASE IF EXISTS {database}")
connection.execute(stmt) connection.execute(stmt)

View File

@ -1,18 +1,18 @@
from sqlalchemy import create_engine from sqlalchemy import create_engine
import subprocess import subprocess
from ..database import engine, _engine_str from ..database import engine, _engine_str
from ..env import getenv from ..env import getenv
from .. import entities from .. import entities
database = getenv("POSTGRES_DATABASE") database = getenv("POSTGRES_DATABASE")
engine = create_engine(_engine_str(), echo=True) engine = create_engine(_engine_str(), echo=True)
"""Application-level SQLAlchemy database engine.""" """Application-level SQLAlchemy database engine."""
# Run Delete and Create Database Scripts # Run Delete and Create Database Scripts
subprocess.run(["python3", "-m", "backend.script.delete_database"]) subprocess.run(["python3", "-m", "backend.script.delete_database"])
subprocess.run(["python3", "-m", "backend.script.create_database"]) subprocess.run(["python3", "-m", "backend.script.create_database"])
entities.EntityBase.metadata.drop_all(engine) entities.EntityBase.metadata.drop_all(engine)
entities.EntityBase.metadata.create_all(engine) entities.EntityBase.metadata.create_all(engine)

View File

@ -1,50 +1,50 @@
"""Shared pytest fixtures for database dependent tests.""" """Shared pytest fixtures for database dependent tests."""
import pytest import pytest
from sqlalchemy import Engine, create_engine, text from sqlalchemy import Engine, create_engine, text
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from ...database import _engine_str from ...database import _engine_str
from ...env import getenv from ...env import getenv
from ... import entities from ... import entities
POSTGRES_DATABASE = f'{getenv("POSTGRES_DATABASE")}_test' POSTGRES_DATABASE = f'{getenv("POSTGRES_DATABASE")}_test'
POSTGRES_USER = getenv("POSTGRES_USER") POSTGRES_USER = getenv("POSTGRES_USER")
def reset_database(): def reset_database():
engine = create_engine(_engine_str(database="")) engine = create_engine(_engine_str(database=""))
with engine.connect() as connection: with engine.connect() as connection:
try: try:
conn = connection.execution_options(autocommit=False) conn = connection.execution_options(autocommit=False)
conn.execute(text("ROLLBACK")) # Get out of transactional mode... conn.execute(text("ROLLBACK")) # Get out of transactional mode...
conn.execute(text(f"DROP DATABASE IF EXISTS {POSTGRES_DATABASE}")) conn.execute(text(f"DROP DATABASE IF EXISTS {POSTGRES_DATABASE}"))
except OperationalError: except OperationalError:
print( print(
"Could not drop database because it's being accessed by others (psql open?)" "Could not drop database because it's being accessed by others (psql open?)"
) )
exit(1) exit(1)
conn.execute(text(f"CREATE DATABASE {POSTGRES_DATABASE}")) conn.execute(text(f"CREATE DATABASE {POSTGRES_DATABASE}"))
conn.execute( conn.execute(
text( text(
f"GRANT ALL PRIVILEGES ON DATABASE {POSTGRES_DATABASE} TO {POSTGRES_USER}" f"GRANT ALL PRIVILEGES ON DATABASE {POSTGRES_DATABASE} TO {POSTGRES_USER}"
) )
) )
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def test_engine() -> Engine: def test_engine() -> Engine:
reset_database() reset_database()
return create_engine(_engine_str(POSTGRES_DATABASE)) return create_engine(_engine_str(POSTGRES_DATABASE))
@pytest.fixture(scope="function") @pytest.fixture(scope="function")
def session(test_engine: Engine): def session(test_engine: Engine):
entities.EntityBase.metadata.drop_all(test_engine) entities.EntityBase.metadata.drop_all(test_engine)
entities.EntityBase.metadata.create_all(test_engine) entities.EntityBase.metadata.create_all(test_engine)
session = Session(test_engine) session = Session(test_engine)
try: try:
yield session yield session
finally: finally:
session.close() session.close()

View File

@ -1,21 +1,21 @@
"""Sample Test File""" """Sample Test File"""
from sqlalchemy import Engine, select from sqlalchemy import Engine, select
from ... import entities from ... import entities
from ...entities.sample_entity import SampleEntity from ...entities.sample_entity import SampleEntity
def test_entity_count(): def test_entity_count():
"""Checks the number of entities to be inserted""" """Checks the number of entities to be inserted"""
print(entities.EntityBase.metadata.tables.keys()) print(entities.EntityBase.metadata.tables.keys())
assert len(entities.EntityBase.metadata.tables.keys()) == 1 assert len(entities.EntityBase.metadata.tables.keys()) == 1
def test_add_sample_data(session: Engine): def test_add_sample_data(session: Engine):
"""Inserts a sample data point and verifies it is in the database""" """Inserts a sample data point and verifies it is in the database"""
entity = SampleEntity(name="Praj", age=19, email="pmoha@unc.edu") entity = SampleEntity(name="Praj", age=19, email="pmoha@unc.edu")
session.add(entity) session.add(entity)
session.commit() session.commit()
data = session.get(SampleEntity, 1) data = session.get(SampleEntity, 1)
assert data.name == "Praj" assert data.name == "Praj"

View File

@ -0,0 +1,19 @@
""" Testing Tag Entity """
from sqlalchemy import Engine
from ... import entities
from ...entities.tag_entity import TagEntity
def test_add_sample_data_tag(session: Engine):
"""Inserts a sample data point and verifies it is in the database"""
entity = TagEntity(content="Test tag")
session.add(entity)
session.commit()
data = session.get(TagEntity, 1)
assert data.id == 1
assert data.content == "Test tag"

View File

@ -0,0 +1,24 @@
""" Testing User Entity """
from sqlalchemy import Engine
from ... import entities
from ...entities.user_entity import UserEntity
from ...entities.user_entity import RoleEnum
from ...entities.user_entity import ProgramEnum
def test_add_sample_data_user(session: Engine):
"""Inserts a sample data point and verifies it is in the database"""
entity = UserEntity(id=1, username="emmalynf", role=RoleEnum.ADMIN, email="efoster@unc.edu", program=[ProgramEnum.COMMUNITY, ProgramEnum.DOMESTIC, ProgramEnum.ECONOMIC], experience=10, group="group")
session.add(entity)
session.commit()
data = session.get(UserEntity, 1)
assert data.id == 1
assert data.username == "emmalynf"
assert data.email == "efoster@unc.edu"
assert data.experience == 10
assert data.role == RoleEnum.ADMIN
assert data.program == [ProgramEnum.COMMUNITY, ProgramEnum.DOMESTIC, ProgramEnum.ECONOMIC]

View File

@ -1,3 +1,3 @@
{ {
"extends": "next/core-web-vitals" "extends": "next/core-web-vitals"
} }

70
compass/.gitignore vendored
View File

@ -1,35 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies # dependencies
/node_modules /node_modules
/.pnp /.pnp
.pnp.js .pnp.js
# testing # testing
/coverage /coverage
# next.js # next.js
/.next/ /.next/
/out/ /out/
# production # production
/build /build
# misc # misc
.DS_Store .DS_Store
*.pem *.pem
# debug # debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
# local env files # local env files
.env*.local .env*.local
# vercel # vercel
.vercel .vercel
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts

View File

@ -1,36 +1,36 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started ## Getting Started
First, run the development server: First, run the development server:
```bash ```bash
npm run dev npm run dev
# or # or
yarn dev yarn dev
# or # or
pnpm dev pnpm dev
# or # or
bun dev bun dev
``` ```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
## Learn More ## Learn More
To learn more about Next.js, take a look at the following resources: To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel ## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@ -1,20 +1,20 @@
import '../styles/globals.css'; import '../styles/globals.css';
import { Metadata } from 'next' import { Metadata } from 'next'
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Login', title: 'Login',
} }
export default function RootLayout({ export default function RootLayout({
// Layouts must accept a children prop. // Layouts must accept a children prop.
// This will be populated with nested layouts or pages // This will be populated with nested layouts or pages
children, children,
}: { }: {
children: React.ReactNode children: React.ReactNode
}) { }) {
return ( return (
<html lang="en"> <html lang="en">
<body>{children}</body> <body>{children}</body>
</html> </html>
) )
} }

View File

@ -1,41 +1,41 @@
// pages/index.tsx // pages/index.tsx
import Button from '@/components/Button'; import Button from '@/components/Button';
import Input from '@/components/Input' import Input from '@/components/Input'
import InlineLink from '@/components/InlineLink'; import InlineLink from '@/components/InlineLink';
import Paper from '@/components/auth/Paper'; import Paper from '@/components/auth/Paper';
import { Metadata } from 'next' import { Metadata } from 'next'
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Login', title: 'Login',
} }
export default function Page() { export default function Page() {
return ( return (
<> <>
<Paper> <Paper>
<form className="mb-0 mt-6 mb-6 space-y-4 rounded-lg p-4 shadow-lg sm:p-6 lg:p-8 bg-white"> <form className="mb-0 mt-6 mb-6 space-y-4 rounded-lg p-4 shadow-lg sm:p-6 lg:p-8 bg-white">
<div className="mb-4"> <div className="mb-4">
<Input type='email' title="Email" placeholder="janedoe@gmail.com" iconKey={'EmailInputIcon'} /> <Input type='email' title="Email" placeholder="janedoe@gmail.com" iconKey={'EmailInputIcon'} />
</div> </div>
<div className="mb-6"> <div className="mb-6">
<Input type='password' title="Password" /> <Input type='password' title="Password" />
</div> </div>
<div className="flex flex-col items-left space-y-4"> <div className="flex flex-col items-left space-y-4">
<InlineLink> <InlineLink>
Forgot password? Forgot password?
</InlineLink> </InlineLink>
<Button> <Button>
Login Login
</Button> </Button>
</div> </div>
</form> </form>
<p className="text-center mt-6 text-gray-500 text-xs"> <p className="text-center mt-6 text-gray-500 text-xs">
&copy; 2024 Compass Center &copy; 2024 Compass Center
</p> </p>
</Paper> </Paper>
</> </>
); );
}; };

View File

@ -1,23 +1,23 @@
import { FunctionComponent, ReactNode } from 'react'; import { FunctionComponent, ReactNode } from 'react';
type ButtonProps = { type ButtonProps = {
children: ReactNode; children: ReactNode;
onClick?: () => void; // make the onClick handler optional onClick?: () => void; // make the onClick handler optional
}; };
const Button: FunctionComponent<ButtonProps> = ({ children, onClick }) => { const Button: FunctionComponent<ButtonProps> = ({ children, onClick }) => {
return ( return (
<button <button
// className="px-4 py-2 font-bold text-white bg-purple-600 rounded hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-opacity-var focus:ring-color-var" // className="px-4 py-2 font-bold text-white bg-purple-600 rounded hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-opacity-var focus:ring-color-var"
className="inline-block rounded border border-purple-600 bg-purple-600 px-12 py-3 text-sm font-semibold text-white hover:bg-transparent hover:text-purple-600 focus:outline-none focus:ring active:text-purple-500" className="inline-block rounded border border-purple-600 bg-purple-600 px-12 py-3 text-sm font-semibold text-white hover:bg-transparent hover:text-purple-600 focus:outline-none focus:ring active:text-purple-500"
onClick={onClick} onClick={onClick}
// style={{ // style={{
// '--ring-opacity-var': `var(--ring-opacity)`, // '--ring-opacity-var': `var(--ring-opacity)`,
// '--ring-color-var': `rgba(var(--ring-color), var(--ring-opacity))` // '--ring-color-var': `rgba(var(--ring-color), var(--ring-opacity))`
// }} // }}
> >
{children} {children}
</button> </button>
); );
}; };
export default Button; export default Button;

View File

@ -1,16 +1,16 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
interface Link { interface Link {
href?: string; href?: string;
children: ReactNode; children: ReactNode;
} }
const InlineLink: React.FC<Link> = ({href = '#', children}) => { const InlineLink: React.FC<Link> = ({href = '#', children}) => {
return ( return (
<a href={href} className='text-sm text-purple-600 hover:underline font-semibold italic'> <a href={href} className='text-sm text-purple-600 hover:underline font-semibold italic'>
{children} {children}
</a> </a>
) )
} }
export default InlineLink; export default InlineLink;

View File

@ -1,42 +1,42 @@
import { Icons } from '@/utils/constants'; import { Icons } from '@/utils/constants';
import React, { FunctionComponent, InputHTMLAttributes, ReactElement, ReactNode } from 'react'; import React, { FunctionComponent, InputHTMLAttributes, ReactElement, ReactNode } from 'react';
type InputProps = InputHTMLAttributes<HTMLInputElement> & { type InputProps = InputHTMLAttributes<HTMLInputElement> & {
iconKey?: keyof typeof Icons; // Use keyof typeof to ensure the key exists in Icons iconKey?: keyof typeof Icons; // Use keyof typeof to ensure the key exists in Icons
title?: string; // Assuming title is always a string title?: string; // Assuming title is always a string
type?: string; type?: string;
placeholder?: string; placeholder?: string;
}; };
const Input: FunctionComponent<InputProps> = ({ iconKey, type, title, placeholder, ...rest }) => { const Input: FunctionComponent<InputProps> = ({ iconKey, type, title, placeholder, ...rest }) => {
const IconComponent = iconKey ? Icons[iconKey] : null; const IconComponent = iconKey ? Icons[iconKey] : null;
return ( return (
<div className="mb-4"> <div className="mb-4">
{title && ( {title && (
<div className="mb-1"> <div className="mb-1">
<label htmlFor={title} className="text-sm font-semibold text-gray-700"> <label htmlFor={title} className="text-sm font-semibold text-gray-700">
{title} {title}
</label> </label>
</div> </div>
)} )}
<div className="flex items-center border border-gray-300 rounded-md shadow-sm overflow-hidden"> <div className="flex items-center border border-gray-300 rounded-md shadow-sm overflow-hidden">
{IconComponent && ( {IconComponent && (
<span className="inline-flex items-center px-3 border-r border-gray-300 text-gray-500"> <span className="inline-flex items-center px-3 border-r border-gray-300 text-gray-500">
<IconComponent className="h-5 w-5" /> <IconComponent className="h-5 w-5" />
</span> </span>
)} )}
<input <input
{...rest} {...rest}
type={type} type={type}
id={title} id={title}
placeholder={placeholder} placeholder={placeholder}
className="w-full border-none p-3 text-sm focus:ring-0" className="w-full border-none p-3 text-sm focus:ring-0"
style={{ boxShadow: 'none' }} // This ensures that the input doesn't have an inner shadow style={{ boxShadow: 'none' }} // This ensures that the input doesn't have an inner shadow
/> />
</div> </div>
</div> </div>
); );
}; };
export default Input; export default Input;

View File

@ -1,15 +1,15 @@
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
interface PageInterface { interface PageInterface {
children: ReactNode; children: ReactNode;
} }
const Paper: React.FC<PageInterface> = ({ children }) => { const Paper: React.FC<PageInterface> = ({ children }) => {
return ( return (
<div className="w-full min-h-screen px-4 py-16 bg-gray-100 sm:px-6 lg:px-8"> <div className="w-full min-h-screen px-4 py-16 bg-gray-100 sm:px-6 lg:px-8">
{children} {children}
</div> </div>
); );
}; };
export default Paper; export default Paper;

View File

@ -1,4 +1,4 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = {} const nextConfig = {}
module.exports = nextConfig module.exports = nextConfig

8730
compass/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,28 @@
{ {
"name": "compass", "name": "compass",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.1.1", "@heroicons/react": "^2.1.1",
"next": "13.5.6", "next": "13.5.6",
"react": "^18", "react": "^18",
"react-dom": "^18" "react-dom": "^18"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
"autoprefixer": "^10", "autoprefixer": "^10",
"eslint": "^8", "eslint": "^8",
"eslint-config-next": "13.5.6", "eslint-config-next": "13.5.6",
"postcss": "^8", "postcss": "^8",
"tailwindcss": "^3", "tailwindcss": "^3",
"typescript": "^5" "typescript": "^5"
} }
} }

View File

@ -1,6 +1,6 @@
module.exports = { module.exports = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
}, },
} }

View File

@ -1,47 +1,47 @@
/* globals.css */ /* globals.css */
@import 'tailwindcss/base'; @import 'tailwindcss/base';
@import 'tailwindcss/components'; @import 'tailwindcss/components';
@import 'tailwindcss/utilities'; @import 'tailwindcss/utilities';
:root { :root {
/* Colors */ /* Colors */
--ring-color: 199, 21, 133; --ring-color: 199, 21, 133;
/* This is the RGB value for a purple color */ /* This is the RGB value for a purple color */
--ring-opacity: 0.5; --ring-opacity: 0.5;
/* Shadows */ /* Shadows */
--shadow-default: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); --shadow-default: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--shadow-focus: 0 0 0 3px rgba(66, 153, 225, 0.5); --shadow-focus: 0 0 0 3px rgba(66, 153, 225, 0.5);
/* Borders */ /* Borders */
--border-radius: 0.375rem; --border-radius: 0.375rem;
/* 6px */ /* 6px */
--border-width: 1px; --border-width: 1px;
/* Spacing */ /* Spacing */
--spacing-px: 1px; --spacing-px: 1px;
--spacing-2: 0.5rem; --spacing-2: 0.5rem;
/* 8px */ /* 8px */
--spacing-3: 0.75rem; --spacing-3: 0.75rem;
/* 12px */ /* 12px */
/* Font */ /* Font */
--font-color: #4a5568; --font-color: #4a5568;
/* A shade of gray */ /* A shade of gray */
} }
@font-face { @font-face {
font-family: 'Inter'; font-family: 'Inter';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: url('/fonts/Inter-Regular.ttf') format('ttf'), src: url('/fonts/Inter-Regular.ttf') format('ttf'),
url('/fonts/Inter-Bold.ttf') format('ttf'), url('/fonts/Inter-Bold.ttf') format('ttf'),
url('/fonts/Inter-Black.ttf') format('ttf'), url('/fonts/Inter-Black.ttf') format('ttf'),
url('/fonts/Inter-ExtraBold.ttf') format('ttf'), url('/fonts/Inter-ExtraBold.ttf') format('ttf'),
url('/fonts/Inter-ExtraLight.ttf') format('ttf'), url('/fonts/Inter-ExtraLight.ttf') format('ttf'),
url('/fonts/Inter-Medium.ttf') format('ttf'), url('/fonts/Inter-Medium.ttf') format('ttf'),
url('/fonts/Inter-SemiBold.ttf') format('ttf'), url('/fonts/Inter-SemiBold.ttf') format('ttf'),
url('/fonts/Inter-Thin.ttf') format('ttf'); url('/fonts/Inter-Thin.ttf') format('ttf');
} }

View File

@ -1,23 +1,23 @@
import type { Config } from 'tailwindcss'; import type { Config } from 'tailwindcss';
const config: Config = { const config: Config = {
content: [ content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}', './pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}',
], ],
theme: { theme: {
extend: { extend: {
backgroundImage: { backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
}, },
fontFamily: { fontFamily: {
sans: ['Inter', 'sans-serif'], sans: ['Inter', 'sans-serif'],
}, },
}, },
}, },
plugins: [], plugins: [],
}; };
export default config; export default config;

View File

@ -1,27 +1,27 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"noEmit": true, "noEmit": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve",
"incremental": true, "incremental": true,
"plugins": [ "plugins": [
{ {
"name": "next" "name": "next"
} }
], ],
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]
} }

View File

@ -1,16 +1,16 @@
class CollectionImpl { class CollectionImpl {
title: string; title: string;
icon: any; icon: any;
data: any; data: any;
constructor(title: string, icon: any) { constructor(title: string, icon: any) {
this.title = title; this.title = title;
this.icon = icon; this.icon = icon;
} }
// subject to change // subject to change
setData(data: any){ setData(data: any){
this.data = data; this.data = data;
} }
} }

View File

@ -1,54 +1,54 @@
import { ListBulletIcon, HashtagIcon, Bars3BottomLeftIcon, EnvelopeIcon, AtSymbolIcon, ClipboardIcon, ArrowsUpDownIcon, ChevronDoubleRightIcon, ChevronDoubleLeftIcon, ChevronRightIcon, ChevronLeftIcon, EyeIcon, EyeSlashIcon, UserIcon, BookOpenIcon, MagnifyingGlassIcon, LinkIcon } from '@heroicons/react/24/solid'; import { ListBulletIcon, HashtagIcon, Bars3BottomLeftIcon, EnvelopeIcon, AtSymbolIcon, ClipboardIcon, ArrowsUpDownIcon, ChevronDoubleRightIcon, ChevronDoubleLeftIcon, ChevronRightIcon, ChevronLeftIcon, EyeIcon, EyeSlashIcon, UserIcon, BookOpenIcon, MagnifyingGlassIcon, LinkIcon } from '@heroicons/react/24/solid';
export const Icons = { export const Icons = {
EmailInputIcon: EnvelopeIcon, EmailInputIcon: EnvelopeIcon,
HidePasswordIcon: EyeSlashIcon, HidePasswordIcon: EyeSlashIcon,
UnhidePasswordIcon: EyeIcon, UnhidePasswordIcon: EyeIcon,
UserIcon: UserIcon, UserIcon: UserIcon,
ResourceIcon: BookOpenIcon, ResourceIcon: BookOpenIcon,
SearchIcon: MagnifyingGlassIcon, SearchIcon: MagnifyingGlassIcon,
ServiceIcon: ClipboardIcon, ServiceIcon: ClipboardIcon,
CloseRightArrow: ChevronDoubleRightIcon, CloseRightArrow: ChevronDoubleRightIcon,
CloseLeftArrow: ChevronDoubleLeftIcon, CloseLeftArrow: ChevronDoubleLeftIcon,
LinkRightArrow:ChevronRightIcon, LinkRightArrow:ChevronRightIcon,
LinkLeftArrow:ChevronLeftIcon, LinkLeftArrow:ChevronLeftIcon,
SortIcon: ArrowsUpDownIcon, SortIcon: ArrowsUpDownIcon,
EmailTableIcon:AtSymbolIcon, EmailTableIcon:AtSymbolIcon,
LinkTableIcon: LinkIcon, LinkTableIcon: LinkIcon,
TextTableIcon: Bars3BottomLeftIcon, TextTableIcon: Bars3BottomLeftIcon,
NumberTableIcon: HashtagIcon, NumberTableIcon: HashtagIcon,
MultiselectTableIcon: ListBulletIcon MultiselectTableIcon: ListBulletIcon
}; };
export enum User { export enum User {
ADMIN, ADMIN,
EMPLOYEE, EMPLOYEE,
VOLUNTEER VOLUNTEER
} }
export enum COLLECTION { export enum COLLECTION {
RESOURCE, RESOURCE,
SERVICE, SERVICE,
USER USER
} }
export enum PROGRAM { export enum PROGRAM {
DOMESTIC_VIOLENCE, DOMESTIC_VIOLENCE,
ECONOMIC_STABILITY, ECONOMIC_STABILITY,
COMMUNITY_EDUCATION COMMUNITY_EDUCATION
} }
export enum DATATYPE { export enum DATATYPE {
INTEGER, INTEGER,
STRING, STRING,
LINK, LINK,
EMAIL, EMAIL,
MULTISELECT, MULTISELECT,
SELECT SELECT
} }
// export const COLLECTION_MAP: {[key in COLLECTION]: CollectionImpl} = { // export const COLLECTION_MAP: {[key in COLLECTION]: CollectionImpl} = {
// [COLLECTION.RESOURCE]: new CollectionImpl('Resources', Icons.ResourceIcon), // [COLLECTION.RESOURCE]: new CollectionImpl('Resources', Icons.ResourceIcon),
// [COLLECTION.SERVICE]: new CollectionImpl('Services', Icons.ServiceIcon), // [COLLECTION.SERVICE]: new CollectionImpl('Services', Icons.ServiceIcon),
// [COLLECTION.USER]: new CollectionImpl('Users', Icons.UserIcon) // [COLLECTION.USER]: new CollectionImpl('Users', Icons.UserIcon)
// } // }