Skip to content

Commit

Permalink
Merge pull request #2 from neilshaabi/1.1_client-model
Browse files Browse the repository at this point in the history
Created client model + role selection buttons + sidebar
  • Loading branch information
neilshaabi authored Feb 23, 2024
2 parents e6fa2d7 + 10b2d9e commit 936309e
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 100 deletions.
114 changes: 54 additions & 60 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

@login_manager.user_loader
def load_user(user_id: str):
return User.query.get(int(user_id))
return db.session.execute(db.select(User).filter_by(id=int(user_id))).scalar_one()


@unique
Expand All @@ -35,6 +35,13 @@ class Gender(Enum):
NON_BINARY = "Non-Binary"


client_issue = sa.Table(
"client_issue",
db.Model.metadata,
sa.Column("client_id", sa.ForeignKey("client.id"), primary_key=True),
sa.Column("issue_id", sa.ForeignKey("issue.id"), primary_key=True),
)

therapist_language = sa.Table(
"therapist_language",
db.Model.metadata,
Expand All @@ -49,13 +56,11 @@ class Gender(Enum):
sa.Column("session_format", sa.Enum(SessionFormat), primary_key=True),
)

therapist_specialisation = sa.Table(
"therapist_specialisation",
therapist_issue = sa.Table(
"therapist_issue",
db.Model.metadata,
sa.Column("therapist_id", sa.ForeignKey("therapist.id"), primary_key=True),
sa.Column(
"specialisation_id", sa.ForeignKey("specialisation.id"), primary_key=True
),
sa.Column("issue_id", sa.ForeignKey("issue.id"), primary_key=True),
)

therapist_intervention = sa.Table(
Expand All @@ -78,10 +83,22 @@ class User(UserMixin, db.Model):
active: so.Mapped[bool] = so.mapped_column(sa.Boolean, default=True)
gender: so.Mapped[Optional["Gender"]] = so.mapped_column(sa.Enum(Gender))
photo_url: so.Mapped[Optional[str]] = so.mapped_column(sa.String(255))
timezone: so.Mapped[Optional[str]] = so.mapped_column(sa.String(50)) # IANA Time Zone Database name
currency: so.Mapped[Optional[str]] = so.mapped_column(sa.String(3)) # ISO 4217 currency code

client: so.Mapped[Optional["Client"]] = so.relationship(back_populates="user")
therapist: so.Mapped[Optional["Therapist"]] = so.relationship(back_populates="user")

def __repr__(self) -> str:
return f"<User({self.id}: {self.email}>"

class Client(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
user_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey('user.id'), index=True)
preferred_gender: so.Mapped[Optional["Gender"]] = so.mapped_column(sa.Enum(Gender))
preferred_language_id: so.Mapped[Optional[int]] = so.mapped_column(sa.ForeignKey('language.id'))

user: so.Mapped["User"] = so.relationship(back_populates="client")
issues: so.Mapped[List["Issue"]] = so.relationship(secondary=client_issue, back_populates="clients")
preferred_language: so.Mapped[Optional["Language"]] = so.relationship("Language")


class Therapist(db.Model):
Expand All @@ -95,95 +112,72 @@ class Therapist(db.Model):
registrations: so.Mapped[Optional[str]] = so.mapped_column(sa.Text)
qualifications: so.Mapped[Optional[str]] = so.mapped_column(sa.Text)
years_of_experience: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer)

user: so.Mapped["User"] = so.relationship(back_populates="therapist")
languages: so.Mapped[List["Language"]] = so.relationship(
secondary=therapist_language, back_populates="therapists"
)
specialisations: so.Mapped[List["Specialisation"]] = so.relationship(
secondary=therapist_specialisation, back_populates="therapists"
)
interventions: so.Mapped[List["Intervention"]] = so.relationship(
secondary=therapist_intervention, back_populates="therapists"
)
session_types: so.Mapped[List["SessionType"]] = so.relationship(
back_populates="therapist"
)
availabilities: so.Mapped[List["Availability"]] = so.relationship(
back_populates="therapist"
)
unavailabilities: so.Mapped[List["Unavailability"]] = so.relationship(
back_populates="therapist"
)
languages: so.Mapped[List["Language"]] = so.relationship(secondary=therapist_language, back_populates="therapists")
specialisations: so.Mapped[List["Issue"]] = so.relationship(secondary=therapist_issue, back_populates="therapists")
interventions: so.Mapped[List["Intervention"]] = so.relationship(secondary=therapist_intervention, back_populates="therapists")
session_types: so.Mapped[List["SessionType"]] = so.relationship(back_populates="therapist")
availabilities: so.Mapped[List["Availability"]] = so.relationship(back_populates="therapist")
unavailabilities: so.Mapped[List["Unavailability"]] = so.relationship(back_populates="therapist")


class Language(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
name: so.Mapped[str] = so.mapped_column(sa.String(50), unique=True)
therapists: so.Mapped[List["Therapist"]] = so.relationship(
secondary="therapist_language", back_populates="languages"
)
iso639_1: so.Mapped[Optional[str]] = so.mapped_column(sa.String(2), unique=True) # ISO 639-1 two-letter code
iso639_2: so.Mapped[Optional[str]] = so.mapped_column(sa.String(3), unique=True) # ISO 639-2 three-letter code

therapists: so.Mapped[List["Therapist"]] = so.relationship(secondary=therapist_language, back_populates="languages")


class Specialisation(db.Model):
class Issue(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
name: so.Mapped[str] = so.mapped_column(sa.String(50), unique=True)
therapists: so.Mapped[List["Therapist"]] = so.relationship(
secondary=therapist_specialisation, back_populates="specialisations"
)

clients: so.Mapped[List["Client"]] = so.relationship(secondary=client_issue, back_populates="issues")
therapists: so.Mapped[List["Therapist"]] = so.relationship(secondary=therapist_issue, back_populates="specialisations")


class Intervention(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
name: so.Mapped[str] = so.mapped_column(sa.String(50), unique=True)
therapists: so.Mapped[List["Therapist"]] = so.relationship(
secondary=therapist_intervention, back_populates="interventions"
)

therapists: so.Mapped[List["Therapist"]] = so.relationship(secondary=therapist_intervention, back_populates="interventions")


class SessionType(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
therapist_id: so.Mapped[int] = so.mapped_column(
sa.ForeignKey("therapist.id"), index=True
)
name: so.Mapped[str] = so.mapped_column(
sa.String(255)
) # e.g. "Initial Consultation"
session_duration: so.Mapped[int] = so.mapped_column(
sa.Integer
) # In minutes
therapist_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey("therapist.id"), index=True)
name: so.Mapped[str] = so.mapped_column(sa.String(255)) # e.g. "Initial Consultation"
session_duration: so.Mapped[int] = so.mapped_column(sa.Integer) # In minutes
fee_amount: so.Mapped[float] = so.mapped_column(sa.Float)
fee_currency: so.Mapped[str] = so.mapped_column(sa.String(3))
session_format: so.Mapped[Optional["SessionFormat"]] = so.mapped_column(sa.Enum(SessionFormat))
notes: so.Mapped[Optional[str]] = so.mapped_column(sa.Text)

therapist: so.Mapped["Therapist"] = so.relationship(back_populates="session_types")


class Availability(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
therapist_id: so.Mapped[int] = so.mapped_column(
sa.ForeignKey("therapist.id"), index=True
)
day_of_week: so.Mapped[Optional[int]] = so.mapped_column(
sa.Integer
) # 0=Monday, 6=Sunday, None for specific dates
therapist_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey("therapist.id"), index=True)
day_of_week: so.Mapped[Optional[int]] = so.mapped_column(sa.Integer) # 0=Monday, 6=Sunday, None for specific dates
start_time: so.Mapped[Optional[time]] = so.mapped_column(sa.Time)
end_time: so.Mapped[Optional[time]] = so.mapped_column(sa.Time)
specific_date: so.Mapped[Optional[date]] = so.mapped_column(
sa.Date
) # For non-recurring availability
specific_date: so.Mapped[Optional[date]] = so.mapped_column(sa.Date) # For non-recurring availability

therapist: so.Mapped["Therapist"] = so.relationship(back_populates="availabilities")


class Unavailability(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
therapist_id: so.Mapped[int] = so.mapped_column(
sa.ForeignKey("therapist.id"), index=True
)
therapist_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey("therapist.id"), index=True)
start_date: so.Mapped[date] = so.mapped_column(sa.Date)
end_date: so.Mapped[date] = so.mapped_column(sa.Date)
reason: so.Mapped[Optional[str]] = so.mapped_column(sa.Text)
therapist: so.Mapped["Therapist"] = so.relationship(
back_populates="unavailabilities"
)

therapist: so.Mapped["Therapist"] = so.relationship(back_populates="unavailabilities")


def insertDummyData() -> None:
Expand Down
107 changes: 91 additions & 16 deletions app/static/css/main.css
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
/* General */

:root {
--accent-primary: #635bff;
--accent-dark: #533afd;
--colour-primary: #635bff;
--colour-dark: #533afd;
--colour-primary-rgb: 99, 91, 255;

--light-grey-bg: #f8fafc;
--light-grey-outline: #e0e1e4;
--light-grey-text: #6c7888;
--dark-grey-text: #414552;

--my-nav-height: 56px;
--navbar-height: 56px;
--sidebar-width: 200px;

--my-form-padding: 1rem 0.75rem;
--my-form-height: calc(3.5rem + 2px);

Expand Down Expand Up @@ -39,27 +42,42 @@ body {

main {
position: absolute;
top: 56px; /* Nav height */
top: 0;
left: 0;
width: 100%;
min-height: calc(100% - var(--my-nav-height));
min-height: 100%;
padding: 3rem;
}

main.navbar-layout {
top: var(--navbar-height);
}

main.sidebar-layout {
left: var(--sidebar-width);
}


/* Navbar */

.navbar {
.navbar,
.sidebar {
position: absolute;
top: 0;
left: 0;
z-index: 1000;
width: 100%;
background: white;
box-shadow: var(--my-box-shadow);
padding: 0.5rem 0;
}

.navbar {
min-height: var(--navbar-height);
width: 100%;
}

.navbar-brand {
font-size: 1.25rem;
color: var(--dark-grey-text) !important;
}

Expand All @@ -68,19 +86,28 @@ main {
margin-right: 3px;
}

.navbar .nav-link {
.nav-link {
color: var(--light-grey-text);
transition: var(--my-transition);
}

.navbar .nav-link:hover {
.nav-link:hover {
color: var(--dark-grey-text);
}

.navbar .nav-link.active {
color: var(--accent-primary);
.nav-link.active {
color: var(--colour-primary);
}

/* Sidebar */

.sidebar {
height: 100%;
width: var(--sidebar-width);
max-width: var(--sidebar-width);
}


/* Text */

h3 {
Expand All @@ -89,13 +116,13 @@ h3 {
}

a {
color: var(--accent-primary);
color: var(--colour-primary);
text-decoration: none;
transition: var(--my-transition);
}

a:hover {
color: var(--accent-dark);
color: var(--colour-dark);
}

i {
Expand Down Expand Up @@ -124,7 +151,7 @@ i {
.form-control:focus {
color: var(--dark-grey-text);
border-color: var(--dark-grey-text);
border-color: var(--accent-primary);
border-color: var(--colour-primary);
outline: none;
box-shadow: none;
}
Expand All @@ -148,6 +175,22 @@ input:-webkit-autofill:focus {
margin: 12px 0;
}

.radio-group {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
gap: 10px;
}

.radio-group.input-error {
border: none !important;
}

.radio-group.input-error .btn {
border: solid 1px red !important;
}


/* Buttons */

Expand All @@ -156,6 +199,9 @@ input:-webkit-autofill:focus {
border: none;
border-radius: var(--my-border-radius);
transition: var(--my-transition);
display: flex;
justify-content: center;
align-items: center;
}

.btn:focus,
Expand All @@ -169,13 +215,41 @@ input:-webkit-autofill:focus {
.btn-primary,
.btn-primary:disabled {
color: white;
background: var(--accent-primary);
background: var(--colour-primary);
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
background: var(--accent-dark);
background: var(--colour-dark);
}

.btn-outline-primary {
color: var(--light-grey-text);
background: transparent;
border: solid 1px var(--light-grey-outline);
}

.btn-check + .btn i {
color: var(--colour-primary);
margin-right: 7px;
display: none;
}

.btn-check:checked + .btn i {
display: inline;
}

.btn-check + .btn:hover {
color: var(--light-grey-text);
background: rgba(var(--colour-primary-rgb), 0.1);
border: solid 1px var(--light-grey-outline);
}

.btn-check:checked + .btn {
color: var(--colour-primary);
background: rgba(var(--colour-primary-rgb), 0.1);
border: solid 1px var(--colour-primary) !important;
}

.form-floating button {
Expand Down Expand Up @@ -276,4 +350,5 @@ input:-webkit-autofill:focus {
width: 100%;
border-radius: 0;
text-align: left;
box-shadow: var(--my-box-shadow);
}
Loading

0 comments on commit 936309e

Please sign in to comment.