Skip to content

Commit

Permalink
fix: hover-state icons, display of course titles, featuring/fav funct…
Browse files Browse the repository at this point in the history
…ionality

* fix: move course title before platform provider within Grid view and remove bullet point when no start/end date

* fix: add cursor-pointer CSS style onto interactive elements per #617

* fix: add featuring libraries to resident view when library is featured per #592 and display featured content to other admins per #629

* fix: modify query to allow resident to favorite featured libraries

* fix: modify query to allow resident to favorite featured libraries and modified libaries query to display featured libraries to other admins at the same facility
  • Loading branch information
carddev81 authored and PThorpe92 committed Jan 9, 2025
1 parent 2948b37 commit a40df16
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 65 deletions.
22 changes: 16 additions & 6 deletions backend/src/database/libraries.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package database

import (
"UnlockEdv2/src/models"
"fmt"
"strings"

log "github.com/sirupsen/logrus"
Expand All @@ -12,20 +13,29 @@ type LibraryResponse struct {
IsFavorited bool `json:"is_favorited"`
}

func (db *DB) GetAllLibraries(page, perPage int, userId, facilityId uint, visibility, orderBy, search string, days int) (int64, []LibraryResponse, error) {
var total int64
func (db *DB) GetAllLibraries(page, perPage, days int, userId, facilityId uint, visibility, orderBy, search string, isAdmin bool) (int64, []LibraryResponse, error) {
var (
total int64
criteria string
id uint
)
libraries := make([]LibraryResponse, 0, perPage)

tx := db.Model(&models.Library{}).Preload("OpenContentProvider").Select(`
//added the below to display featuring flags for all admins per facility
selectIsFavoriteOrIsFeatured := `
libraries.*,
EXISTS (
SELECT 1
FROM open_content_favorites f
WHERE f.content_id = libraries.id
AND f.open_content_provider_id = libraries.open_content_provider_id
AND f.user_id = ?
) AS is_favorited`, userId)
AND %s) AS is_favorited`

if isAdmin {
criteria, id = "f.facility_id = ?", facilityId
} else {
criteria, id = "f.user_id = ?", userId
}
tx := db.Model(&models.Library{}).Preload("OpenContentProvider").Select(fmt.Sprintf(selectIsFavoriteOrIsFeatured, criteria), id)
visibility = strings.ToLower(visibility)

isFeatured := false
Expand Down
10 changes: 7 additions & 3 deletions backend/src/database/videos.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,22 @@ func (db *DB) FavoriteOpenContent(contentID int, ocpID uint, userID uint, facili
UserID: userID,
FacilityID: facilityID,
}
//use Unscoped method to ignore soft deletions
//added user id to query below to isolate favorite by user when facility id is passed in
tx := db.Where("content_id = ? AND open_content_provider_id = ?", contentID, ocpID)
if facilityID != nil {
tx = tx.Where("facility_id = ?", facilityID)
} else {
tx = tx.Where("user_id = ?", userID)
}
if tx.First(&models.OpenContentFavorite{}).RowsAffected > 0 {
delTx := db.Where("content_id = ? AND user_id = ? AND open_content_provider_id = ?", contentID, userID, ocpID)
delTx := db.Where("content_id = ? AND open_content_provider_id = ?", contentID, ocpID)
if facilityID != nil {
delTx = delTx.Where("facility_id = ?", facilityID)
} else {
delTx = delTx.Where("user_id = ?", userID)
}
if err := delTx.Delete(&fav).Error; err != nil {
return false, newDeleteDBError(err, "video_favorites")
return false, newDeleteDBError(err, "open_content_favorites")
}
return false, nil
} else {
Expand Down
7 changes: 4 additions & 3 deletions backend/src/handlers/libraries_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ func (srv *Server) handleIndexLibraries(w http.ResponseWriter, r *http.Request,
showHidden := "visible"
if !userIsAdmin(r) && r.URL.Query().Get("visibility") == "hidden" {
return newUnauthorizedServiceError()
}
if userIsAdmin(r) {
} else if !userIsAdmin(r) && r.URL.Query().Get("visibility") == "featured" {
showHidden = "featured"
} else if userIsAdmin(r) {
showHidden = r.URL.Query().Get("visibility")
}
claims := r.Context().Value(ClaimsKey).(*Claims)
total, libraries, err := srv.Db.GetAllLibraries(page, perPage, claims.UserID, claims.FacilityID, showHidden, orderBy, search, days)
total, libraries, err := srv.Db.GetAllLibraries(page, perPage, days, claims.UserID, claims.FacilityID, showHidden, orderBy, search, claims.isAdmin())
if err != nil {
return newDatabaseServiceError(err)
}
Expand Down
15 changes: 7 additions & 8 deletions frontend/src/Components/CatalogCourseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,9 @@ export default function CatalogCourseCard({
})
: '';
const finalDateStr =
' • ' +
courseStartDtStr +
(courseStartDt || courseEndDt ? ' - ' : '') +
courseEndDtStr;
courseStartDt || courseEndDt
? ' • ' + courseStartDtStr + ' - ' + courseEndDtStr
: '';
if (view == ViewType.List) {
return (
<a
Expand Down Expand Up @@ -113,13 +112,13 @@ export default function CatalogCourseCard({
</figure>
<div className="card-body gap-0.5">
{/* this should be the school or course that offers the course */}
<p className="text-xs">
{course.provider_name}
{finalDateStr}
</p>
<h3 className="card-title text-sm">
{course.course_name}
</h3>
<p className="text-xs h-6">
{course.provider_name}
{finalDateStr}
</p>
<p className="body-small line-clamp-2">
{course.description}
</p>
Expand Down
51 changes: 30 additions & 21 deletions frontend/src/Components/EnrolledCourseCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,28 @@ export default function EnrolledCourseCard({
const url = course.external_url;
let status: CourseStatus | null = null;
if (course.course_progress == 100) status = CourseStatus.Completed;
const courseStartDt = course.start_dt ? new Date(course.start_dt) : course.start_dt;
const courseStartDt = course.start_dt
? new Date(course.start_dt)
: course.start_dt;
const courseEndDt = course.end_dt ? new Date(course.end_dt) : course.end_dt;
const courseStartDtStr = courseStartDt ? courseStartDt.toLocaleDateString('en-US',
{
year: 'numeric',
month: '2-digit',
day: 'numeric'
})
: ""
const courseEndDtStr = courseEndDt ? courseEndDt.toLocaleDateString('en-US',
{
year: 'numeric',
month: '2-digit',
day: 'numeric'
})
: "";
const finalDateStr = " • " + courseStartDtStr + (courseStartDt || courseEndDt ? " - " : "") + courseEndDtStr
const courseStartDtStr = courseStartDt
? courseStartDt.toLocaleDateString('en-US', {
year: 'numeric',
month: '2-digit',
day: 'numeric'
})
: '';
const courseEndDtStr = courseEndDt
? courseEndDt.toLocaleDateString('en-US', {
year: 'numeric',
month: '2-digit',
day: 'numeric'
})
: '';
const finalDateStr =
courseStartDt || courseEndDt
? ' • ' + courseStartDtStr + ' - ' + courseEndDtStr
: '';
if (view == ViewType.List) {
return (
<a
Expand All @@ -48,7 +53,10 @@ export default function EnrolledCourseCard({
<div className="flex flex-row gap-3 items-center">
<h2>{course.course_name}</h2>
<p className="body">|</p>
<p className="body">{course.provider_platform_name}{finalDateStr}</p>
<p className="body">
{course.provider_platform_name}
{finalDateStr}
</p>
</div>
{status === CourseStatus.Completed ? (
<div className="flex flex-row gap-2 body-small text-teal-3">
Expand Down Expand Up @@ -86,13 +94,14 @@ export default function EnrolledCourseCard({
)}
</figure>
<div className="card-body gap-0.5">
<p className="text-xs line-clamp-2">
{course.provider_platform_name}{finalDateStr}
</p>
<h3 className="card-title text-sm h-10 line-clamp-2">
<h3 className="card-title text-sm line-clamp-2">
{course.alt_name && course.alt_name + ' - '}
{course.course_name}
</h3>
<p className="text-xs h-10 line-clamp-2">
{course.provider_platform_name}
{finalDateStr}
</p>
<div className="mt-3 justify-end">
{status == CourseStatus.Completed ? (
<div className="flex flex-row gap-2 body-small text-teal-3">
Expand Down
45 changes: 27 additions & 18 deletions frontend/src/Components/ProviderCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,16 @@ export default function ProviderCard({
openEditProvider: (prov: ProviderPlatform) => void;
oidcClient: (prov: ProviderPlatform) => void;
showAuthorizationInfo: (prov: ProviderPlatform) => void;
refreshToken: (prov: ProviderPlatform) => void;
refreshToken: (prov: ProviderPlatform) => void;
archiveProvider: (prov: ProviderPlatform) => void;
}) {
const navigate = useNavigate();
return (
<tr className="bg-base-teal card p-4 w-full grid-cols-4 justify-items-center">
<td className="justify-self-start">{provider.name}</td>
<td>
{provider.oidc_id !== 0 || provider.type === ProviderPlatformType.BRIGHTSPACE ? (
{provider.oidc_id !== 0 ||
provider.type === ProviderPlatformType.BRIGHTSPACE ? (
<CheckCircleIcon className="w-4" />
) : (
<div className="w-4"></div>
Expand All @@ -59,27 +60,31 @@ export default function ProviderCard({
<td className="flex flex-row gap-3 justify-self-end">
{provider.state !== ProviderPlatformState.ARCHIVED ? (
<>
{provider.oidc_id !== 0 || provider.type === ProviderPlatformType.BRIGHTSPACE ? (
{provider.oidc_id !== 0 ||
provider.type === ProviderPlatformType.BRIGHTSPACE ? (
<>
{provider.type === ProviderPlatformType.BRIGHTSPACE ? (
{provider.type ===
ProviderPlatformType.BRIGHTSPACE ? (
<ULIComponent
dataTip={'Refresh Token'}
icon={ArrowPathIcon}
onClick={() =>
refreshToken(provider)
}
/>
) : ( <ULIComponent
dataTip={'Auth Info'}
icon={InformationCircleIcon}
onClick={() =>
showAuthorizationInfo(provider)
}
/>)
}
tooltipClassName="cursor-pointer"
dataTip={'Refresh Token'}
icon={ArrowPathIcon}
onClick={() => refreshToken(provider)}
/>
) : (
<ULIComponent
tooltipClassName="cursor-pointer"
dataTip={'Auth Info'}
icon={InformationCircleIcon}
onClick={() =>
showAuthorizationInfo(provider)
}
/>
)}
{provider.type !==
ProviderPlatformType.KOLIBRI && (
<ULIComponent
tooltipClassName="cursor-pointer"
dataTip={'Manage Users'}
icon={UserGroupIcon}
onClick={() =>
Expand All @@ -92,24 +97,28 @@ export default function ProviderCard({
</>
) : (
<ULIComponent
tooltipClassName="cursor-pointer"
dataTip={'Register Provider'}
icon={LinkIcon}
onClick={() => oidcClient(provider)}
/>
)}
<ULIComponent
tooltipClassName="cursor-pointer"
dataTip={'Edit Provider'}
icon={PencilSquareIcon}
onClick={() => openEditProvider(provider)}
/>
<ULIComponent
tooltipClassName="w-4 h-4 self-start cursor-pointer"
dataTip={'Disable'}
icon={XMarkIcon}
onClick={() => archiveProvider(provider)}
/>
</>
) : (
<ULIComponent
tooltipClassName="cursor-pointer"
dataTip={'Enable Provider'}
icon={CheckCircleIcon}
onClick={() => archiveProvider(provider)}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/Pages/AdminManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export default function AdminManagement() {
<div className="flex space-x-4">
<ULIComponent
dataTip={'Edit Admin'}
tooltipClassName="tooltip-left"
tooltipClassName="tooltip-left cursor-pointer"
onClick={() => {
setTargetUser(user);
editUserModal.current?.showModal();
Expand All @@ -246,7 +246,7 @@ export default function AdminManagement() {
dataTip={
'Reset Password'
}
tooltipClassName="tooltip-left"
tooltipClassName="tooltip-left cursor-pointer"
onClick={() => {
setTargetUser(user);
resetUserPasswordModal.current?.showModal();
Expand All @@ -259,7 +259,7 @@ export default function AdminManagement() {
dataTip={getUserIconData[
'data-tip'
](user)}
tooltipClassName="tooltip-left"
tooltipClassName="tooltip-left cursor-pointer"
onClick={getUserIconData.onClick(
user
)}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/Pages/StudentManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export default function StudentManagement() {
<div className="flex space-x-4">
<ULIComponent
dataTip={'Edit Student'}
tooltipClassName="tooltip-left"
tooltipClassName="tooltip-left cursor-pointer"
icon={PencilSquareIcon}
onClick={() => {
setTargetUser(user);
Expand All @@ -228,7 +228,7 @@ export default function StudentManagement() {
dataTip={
'Reset Password'
}
tooltipClassName="tooltip-left"
tooltipClassName="tooltip-left cursor-pointer"
icon={
ArrowPathRoundedSquareIcon
}
Expand All @@ -242,7 +242,7 @@ export default function StudentManagement() {
dataTip={
'Delete Student'
}
tooltipClassName="tooltip-left"
tooltipClassName="tooltip-left cursor-pointer"
icon={TrashIcon}
onClick={() => {
setTargetUser(user);
Expand Down

0 comments on commit a40df16

Please sign in to comment.