Skip to content

Commit

Permalink
Themes: Add support for themes and add dark theme (pupilfirst#1483)
Browse files Browse the repository at this point in the history
Co-authored-by: Raj Suhail <[email protected]>
Co-authored-by: vinu tv <[email protected]>
Co-authored-by: yash-learner <[email protected]>
Co-authored-by: Hari Gopal <[email protected]>
  • Loading branch information
5 people authored Dec 20, 2023
1 parent 271e997 commit 5045deb
Show file tree
Hide file tree
Showing 63 changed files with 1,240 additions and 557 deletions.
2 changes: 1 addition & 1 deletion app/controllers/home_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def oauth_error

# GET /favicon.ico
def favicon
if current_school.present? && current_school.icon.attached?
if current_school.present? && current_school.icon_on_light_bg.attached?
redirect_to(
view_context.rails_public_blob_url(current_school.icon_variant(:thumb)),
allow_other_host: true
Expand Down
60 changes: 54 additions & 6 deletions app/forms/schools/images_form.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,61 @@
module Schools
class ImagesForm < Reform::Form
property :logo_on_light_bg, virtual: true, validates: { image: true, file_size: { less_than: 2.megabytes } }, allow_nil: true
property :cover_image, virtual: true, validates: { image: true, file_size: { less_than: 2.megabytes } }, allow_nil: true
property :icon, virtual: true, validates: { image: true, file_size: { less_than: 2.megabytes } }, allow_nil: true
property :logo_on_light_bg,
virtual: true,
validates: {
image: true,
file_size: {
less_than: 2.megabytes
}
},
allow_nil: true
property :logo_on_dark_bg,
virtual: true,
validates: {
image: true,
file_size: {
less_than: 2.megabytes
}
},
allow_nil: true
property :cover_image,
virtual: true,
validates: {
image: true,
file_size: {
less_than: 2.megabytes
}
},
allow_nil: true
property :icon_on_light_bg,
virtual: true,
validates: {
image: true,
file_size: {
less_than: 2.megabytes
}
},
allow_nil: true
property :icon_on_dark_bg,
virtual: true,
validates: {
image: true,
file_size: {
less_than: 2.megabytes
}
},
allow_nil: true

def save
model.logo_on_light_bg.attach(logo_on_light_bg) if logo_on_light_bg.present?
model.cover_image.attach(cover_image) if cover_image.present?
model.icon.attach(icon) if icon.present?
%i[
logo_on_light_bg
logo_on_dark_bg
cover_image
icon_on_light_bg
icon_on_dark_bg
].each do |image|
model.public_send(image).attach(send(image)) if send(image).present?
end
end
end
end
15 changes: 10 additions & 5 deletions app/frontend/admin/courses/CourseEditor__Root.res
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
%%raw(`import "./CourseEditor__Root.css"`)

open ThemeSwitch

@module("../../shared/images/add-new-course.svg")
external addNewCourseSVG: string = "default"

Expand Down Expand Up @@ -380,11 +382,12 @@ let showCourse = course => {
/>
}}
</div>
<div
className="course-editor-course__title-container absolute w-full flex inset-x-0 bottom-0 p-4 z-10"
key={Course.id(course)}>
<div className="flex gap-2 border-b border-gray-200" key={Course.id(course)}>
<div className="block h-min ms-6 pt-3 pb-2 px-2 bg-primary-100 rounded-b-full">
<PfIcon className="if i-book-solid if-fw text-primary-400" />
</div>
<h4
className="course-editor-course__title text-white font-semibold leading-tight pe-4 text-lg md:text-xl">
className="w-full text-gray-900 font-semibold leading-tight pe-6 py-3 text-lg md:text-xl">
{str(Course.name(course))}
</h4>
</div>
Expand Down Expand Up @@ -604,7 +607,9 @@ let make = (~school) => {
<div className="flex gap-6 px-6">
<div
className="school-overview__logo-container flex items-center bg-white p-3 border-4 border-white shadow-md ring-1 ring-gray-100 rounded-full -mt-16 overflow-hidden">
{switch School.logoUrl(school) {
{switch getTheme() == "light"
? School.logoOnLightBgUrl(school)
: School.logoOnDarkBgUrl(school) {
| Some(url) =>
<img
className="h-9 md:h-12 object-contain flex text-sm items-center"
Expand Down
73 changes: 37 additions & 36 deletions app/frontend/admin/courses/StudentBulkImport__Root.res
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,19 @@ let fileInputText = (~fileInfo: option<fileInfo>) =>

let reducer = (state, action) =>
switch action {
| UpdateFileInvalid(fileInvalid) => {...state, fileInvalid: fileInvalid}
| UpdateFileInvalid(fileInvalid) => {...state, fileInvalid}
| ClearCSVData => {...state, fileInfo: None, fileInvalid: None, csvData: []}
| ToggleNotifyStudents => {...state, notifyStudents: !state.notifyStudents}
| BeginSaving => {...state, saving: true}
| FailSaving => {...state, saving: false}
| EndSaving => initialState
| LoadCSVData(csvData, fileInfo) => {
...state,
csvData: csvData,
csvData,
fileInfo: Some(fileInfo),
fileInvalid: validateFile(csvData, fileInfo),
}
| SetBaseData(cohorts) => {...state, cohorts: cohorts, loading: false}
| SetBaseData(cohorts) => {...state, cohorts, loading: false}
| SetSelectedCohort(cohort) => {...state, selectedCohort: Some(cohort)}
| SetLoading => {...state, loading: true}
| ClearLoading => {...state, loading: false}
Expand Down Expand Up @@ -207,41 +207,40 @@ let tableHeader = {
}

let tableRows = (csvData, ~startingRow=0, ()) => {
Js.Array.mapi(
(studentData, index) =>
<tr key={string_of_int(index)}>
<td className="w-12 bg-gray-300 border border-gray-400 truncate text-xs px-2 py-1">
{string_of_int(startingRow + index + 2)->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.name(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.email(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.title(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.teamName(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.tags(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.affiliation(studentData)->Belt.Option.getWithDefault("")->str}
</td>
</tr>,
csvData,
)->React.array
Js.Array.mapi((studentData, index) =>
<tr key={string_of_int(index)}>
<td className="w-12 bg-gray-300 border border-gray-400 truncate text-xs px-2 py-1">
{string_of_int(startingRow + index + 2)->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.name(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.email(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.title(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.teamName(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.tags(studentData)->Belt.Option.getWithDefault("")->str}
</td>
<td className="border border-gray-400 truncate text-xs px-2 py-1">
{StudentCSVRow.affiliation(studentData)->Belt.Option.getWithDefault("")->str}
</td>
</tr>
, csvData)->React.array
}

let truncatedTable = csvData => {
let firsTwoRows = Js.Array.slice(~start=0, ~end_=2, csvData)
let lastTwoRows = Js.Array.sliceFrom(Js.Array.length(csvData) - 2, csvData)
<div>
<table className="table-fixed mt-2 border w-full overflow-x-scroll">
{tableHeader} <tbody> {tableRows(firsTwoRows, ())} </tbody>
{tableHeader}
<tbody> {tableRows(firsTwoRows, ())} </tbody>
</table>
<table className="table-fixed relative w-full overflow-x-scroll">
<tbody>
Expand Down Expand Up @@ -283,7 +282,8 @@ let csvDataTable = (csvData, fileInvalid) => {
<p className="font-semibold text-xs mt-4"> {t("valid_data_summary_text")->str} </p>
{Js.Array.length(csvData) <= 10
? <table className="table-fixed mt-2 border w-full overflow-x-scroll">
{tableHeader} <tbody> {tableRows(csvData, ())} </tbody>
{tableHeader}
<tbody> {tableRows(csvData, ())} </tbody>
</table>
: truncatedTable(csvData)}
</div>,
Expand All @@ -302,7 +302,8 @@ let rowClasses = hasError =>

let errorsTable = (csvData, errors) => {
<table className="table-fixed mt-4 border w-full overflow-x-scroll">
{tableHeader} <tbody> {Js.Array.mapi((error, index) => {
{tableHeader}
<tbody> {Js.Array.mapi((error, index) => {
let rowNumber = CSVDataError.rowNumber(error)
let studentData = Js.Array2.unsafe_get(csvData, rowNumber - 2)
<tr key={string_of_int(index)}>
Expand Down Expand Up @@ -490,7 +491,7 @@ let make = (~courseId) => {
onClick={_event => clearFile(send, "csv-file-input")}
className="file-input-label mt-2"
htmlFor="csv-file-input">
<i className="fas fa-upload me-2 text-gray-600 text-lg" />
<i className="fas fa-upload me-2 text-primary-300 text-lg" />
<span className="truncate">
{fileInputText(~fileInfo=state.fileInfo)->str}
</span>
Expand Down Expand Up @@ -527,8 +528,8 @@ let make = (~courseId) => {
/>
</div>}
</div>
<div className="flex justify-end px-6 pb-6 mx-auto">
<button disabled={saveDisabled(state)} className="w-auto btn btn-success">
<div className="flex pb-6 mx-auto">
<button disabled={saveDisabled(state)} className="w-auto btn btn-primary">
{t("import_button_text")->str}
</button>
</div>
Expand Down
24 changes: 12 additions & 12 deletions app/frontend/admin/courses/course_editor/CourseEditor__Form.res
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type tabs =

let selectedTabClasses = selected =>
"flex items-center focus:outline-none justify-center w-1/3 p-3 font-semibold rounded-t-lg leading-relaxed border border-gray-300 text-gray-600 cursor-pointer hover:bg-gray-50 hover:text-gray-900 focus:ring-2 focus:ring-inset focus:ring-focusColor-500 " ++ (
selected ? "text-primary-500 bg-white border-b-0" : "bg-gray-50"
selected ? "text-primary-500 bg-white border-b-transparent" : "bg-gray-50"
)

let tabItemsClasses = selected => selected ? "" : "hidden"
Expand Down Expand Up @@ -63,31 +63,31 @@ let reducer = (state, action) =>
| FailSaving => {...state, saving: false}
| UpdateName(name, hasNameError) => {
...state,
name: name,
hasNameError: hasNameError,
name,
hasNameError,
dirty: true,
}
| UpdateDescription(description, hasDescriptionError) => {
...state,
description: description,
hasDescriptionError: hasDescriptionError,
description,
hasDescriptionError,
dirty: true,
}
| UpdatePublicSignup(publicSignup) => {...state, publicSignup: publicSignup, dirty: true}
| UpdatePublicPreview(publicPreview) => {...state, publicPreview: publicPreview, dirty: true}
| UpdateAbout(about) => {...state, about: about, dirty: true}
| UpdateFeatured(featured) => {...state, featured: featured, dirty: true}
| UpdatePublicSignup(publicSignup) => {...state, publicSignup, dirty: true}
| UpdatePublicPreview(publicPreview) => {...state, publicPreview, dirty: true}
| UpdateAbout(about) => {...state, about, dirty: true}
| UpdateFeatured(featured) => {...state, featured, dirty: true}
| UpdateProgressionBehavior(progressionBehavior) => {
...state,
progressionBehavior: progressionBehavior,
progressionBehavior,
dirty: true,
}
| UpdateHighlights(highlights) => {...state, highlights: highlights, dirty: true}
| UpdateHighlights(highlights) => {...state, highlights, dirty: true}
| SetHasProcessingUrl => {...state, hasProcessingUrl: true, dirty: true}
| ClearHasProcessingUrl => {...state, hasProcessingUrl: false, dirty: true}
| UpdateProcessingUrl(processingUrl) => {
...state,
processingUrl: processingUrl,
processingUrl,
dirty: true,
}
| SetCohortsData(cohortsData) => {...state, cohorts: cohortsData, loading: false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ let optionalImageLabelText = (image, selectedFilename) =>
switch image {
| Some(existingImage) =>
<span>
{t("replace_image_label")->str} <code> {Course.filename(existingImage)->str} </code>
{t("replace_image_label")->str}
<code> {Course.filename(existingImage)->str} </code>
</span>
| None => t("empty_image_label") |> str
}
Expand Down Expand Up @@ -164,7 +165,8 @@ let make = (~course, ~updateCourseCB) => {
link={t("thumbnail.help_url")}>
{t("thumbnail.help")->str}
</HelpIcon>
<div className="rounded focus-within:outline-none focus-within:ring-2 focus-within:ring-focusColor-500">
<div
className="rounded focus-within:outline-none focus-within:ring-2 focus-within:ring-focusColor-500">
<input
disabled=state.updating
className="absolute h-0 w-0 focus:outline-none"
Expand All @@ -177,7 +179,7 @@ let make = (~course, ~updateCourseCB) => {
onChange={updateImage(send, false)}
/>
<label className="file-input-label mt-2" htmlFor="course-images-editor__thumbnail">
<i className="fas fa-upload" />
<i className="fas fa-upload text-primary-300 text-lg" />
<span className="ms-2 truncate">
{optionalImageLabelText(thumbnail, state.filenameThumb)}
</span>
Expand All @@ -197,7 +199,8 @@ let make = (~course, ~updateCourseCB) => {
link={t("cover_image.help_url")}>
{t("cover_image.help") |> str}
</HelpIcon>
<div className="rounded focus-within:outline-none focus-within:ring-2 focus-within:ring-focusColor-500">
<div
className="rounded focus-within:outline-none focus-within:ring-2 focus-within:ring-focusColor-500">
<input
disabled=state.updating
className="absolute h-0 w-0 focus:outline-none"
Expand All @@ -210,7 +213,7 @@ let make = (~course, ~updateCourseCB) => {
onChange={updateImage(send, true)}
/>
<label className="file-input-label mt-2" htmlFor="course-images-editor__cover">
<i className="fas fa-upload" />
<i className="fas fa-upload text-primary-300 text-lg" />
<span className="ms-2 truncate">
{optionalImageLabelText(cover, state.filenameCover)}
</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@

.skeleton-animate {
animation: skeletonShimmer 3s infinite linear;
background: linear-gradient(to right, #f5f3f7 10%, #fbfafc 40%, #f5f3f7 70%);
background: linear-gradient(
to right,
theme("colors.gray.100"),
theme("colors.gray.200"),
theme("colors.gray.100")
);
background-size: 1000px 100%;
}

Expand All @@ -142,13 +147,13 @@

@keyframes completeButtonGlowing {
0% {
box-shadow: 0 0 20px #7886d7;
box-shadow: 0 0 20px theme("colors.focusColor.400");
}
50% {
box-shadow: 0 0 10px #7886d7;
box-shadow: 0 0 10px theme("colors.focusColor.400");
}
100% {
box-shadow: 0 0 0 #7886d7;
box-shadow: 0 0 0 theme("colors.focusColor.400");
}
}

Expand Down
2 changes: 1 addition & 1 deletion app/frontend/entrypoints/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ import "~/shared/serviceWorkerRegisterer.js";
import "~/shared/i18n.js";
import * as IconFirst from "../packages/pf-icon/src/iconFirst.js";
import "~/shared/reComponentLoader.js";

import "~/shared/components/ThemeSwitch.bs.js";
IconFirst.addListener();
Loading

0 comments on commit 5045deb

Please sign in to comment.