Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
af73a46
admin logout
Jun 11, 2026
cd694d6
removed npm package lock + yaml file to block non-pnpm package locks
AidanLoran Jun 24, 2026
11276e4
gitignore update
AidanLoran Jul 1, 2026
f35640b
fixed broken zod extend that required a student name on update
AidanLoran Jul 4, 2026
23fff99
added dev dependencies
AidanLoran Jul 4, 2026
2d49dae
TextFormatting
Jul 6, 2026
e0957c6
fix
Jul 8, 2026
5a30169
bye npm
AidanLoran Jul 9, 2026
3bed1b7
Merge pull request #370 from UTDallasEPICS/adminLogout
v-patel Jul 9, 2026
cf098aa
finally?
AidanLoran Jul 9, 2026
6d45aea
bye deprecated action version
AidanLoran Jul 9, 2026
50abfc6
Merge pull request #424 from UTDallasEPICS/buildcheckfix2
v-patel Jul 10, 2026
a56c383
universal-admin UI
nevins321 Jul 12, 2026
ce0d021
Merge branch 'stage' into 389-bug-api-call-to-update-student-exp-afte…
AidanLoran Jul 13, 2026
6317dfe
unnecessary comment gone
AidanLoran Jul 13, 2026
12c541f
seed updated
AidanLoran Jul 13, 2026
c957311
Merge branch 'stage' of https://github.com/UTDallasEPICS/Reading-MLK …
AidanLoran Jul 15, 2026
692cf67
lock fix
AidanLoran Jul 15, 2026
6c727bd
Merge branch 'stage' of https://github.com/UTDallasEPICS/Reading-MLK …
AidanLoran Jul 15, 2026
b978495
Merge pull request #392 from UTDallasEPICS/391-bug-tailwindcss-dev-de…
v-patel Jul 15, 2026
ac9ca83
Merge pull request #387 from UTDallasEPICS/381-standardize-package-ma…
v-patel Jul 15, 2026
bc2a1c5
Merge pull request #421 from UTDallasEPICS/AnnouncementHistoryBug
v-patel Jul 15, 2026
94dc3e6
Merge pull request #390 from UTDallasEPICS/389-bug-api-call-to-update…
v-patel Jul 17, 2026
6f974b3
aidan fixes
nevins321 Jul 18, 2026
13748c4
Merge branch 'stage' of https://github.com/UTDallasEPICS/Reading-MLK …
AidanLoran Jul 18, 2026
ed33297
Merge branch 'teacher-UI' of https://github.com/UTDallasEPICS/Reading…
AidanLoran Jul 18, 2026
8d19a82
Merge branch 'stage' of https://github.com/UTDallasEPICS/Reading-MLK …
AidanLoran Jul 18, 2026
6705704
poster to user relation and removal of redundant "admin" boolean attr…
AidanLoran Jul 18, 2026
e9aa807
added seed to gitignore so no one pushes their email
AidanLoran Jul 18, 2026
e4240e4
oops fixed the migration
AidanLoran Jul 18, 2026
9622537
Merge branch 'stage' into 428-bug-udpate-prismaseedts-with-db-change
AidanLoran Jul 18, 2026
5ce4fa9
dropdown implementation
nevins321 Jul 18, 2026
8abae4d
got rid of the dummy page and fixed redirects
nevins321 Jul 18, 2026
f7a340e
if user is admin it should send them to the universal-admin page
nevins321 Jul 18, 2026
061e432
class data isolation
nevins321 Jul 18, 2026
4b49d8c
Merge pull request #429 from UTDallasEPICS/428-bug-udpate-prismaseedt…
v-patel Jul 18, 2026
000f1a5
Merge pull request #427 from UTDallasEPICS/teacher-UI
v-patel Jul 18, 2026
e29d305
Merge pull request #435 from UTDallasEPICS/433-bug-admin-to-reader-sw…
v-patel Jul 18, 2026
f0742e8
Merge branch 'stage' of https://github.com/UTDallasEPICS/Reading-MLK …
AidanLoran Jul 18, 2026
28ebdc0
aidan fixes
nevins321 Jul 18, 2026
eb4c01e
Merge branch 'stage' into class-data-isolation
nevins321 Jul 18, 2026
7f4a21e
Merge branch 'dropdown-menu-implementation' into class-data-isolation
nevins321 Jul 18, 2026
d3e5d50
fixed from dropdown pr
nevins321 Jul 18, 2026
58b55fa
aidan fixes
nevins321 Jul 19, 2026
21519a7
Merge branch 'dropdown-menu-implementation' into class-data-isolation
nevins321 Jul 19, 2026
68c4580
rename
nevins321 Jul 19, 2026
908a8ee
seperate classContextSelect into 2 files
nevins321 Jul 19, 2026
c55baad
Merge branch 'dropdown-menu-implementation' into class-data-isolation
nevins321 Jul 19, 2026
b4ffcfe
Merge branch 'dropdown-menu-implementation' into class-data-isolation
nevins321 Jul 19, 2026
17f0af7
aidan fix
nevins321 Jul 20, 2026
4af0c64
Merge branch 'renaming' into dropdown-menu-implementation
nevins321 Jul 20, 2026
903e063
Merge pull request #441 from UTDallasEPICS/renaming
v-patel Jul 20, 2026
003ddc6
Merge branch 'stage' into dropdown-menu-implementation
nevins321 Jul 20, 2026
8311efc
small bug fix
nevins321 Jul 20, 2026
1fcec14
fixed
nevins321 Jul 20, 2026
a39c888
Merge pull request #434 from UTDallasEPICS/dropdown-menu-implementation
v-patel Jul 20, 2026
5a43b4f
Merge branch 'stage' into class-data-isolation
nevins321 Jul 21, 2026
e3d6754
bug fix
nevins321 Jul 21, 2026
8761bc0
fixed
nevins321 Jul 21, 2026
f3db324
almost
nevins321 Jul 21, 2026
8499322
fixed now!
nevins321 Jul 21, 2026
30f66a8
Merge branch 'stage' into class-data-isolation
nevins321 Jul 22, 2026
9fabe78
new user fix
nevins321 Jul 22, 2026
fee6b9b
aidan fix
nevins321 Jul 22, 2026
2eb92f4
raw user facing error, removed and fixed
nevins321 Jul 22, 2026
94e8b93
aidan fix pt 2
nevins321 Jul 22, 2026
14f0af4
done
nevins321 Jul 22, 2026
f03e222
Merge pull request #436 from UTDallasEPICS/class-data-isolation
v-patel Jul 23, 2026
c607071
missed in rename migration
nevins321 Jul 27, 2026
1c555d5
coach onboarding and teacher verification
nevins321 Jul 27, 2026
8e6f63a
Merge pull request #442 from UTDallasEPICS/needed-migration
v-patel Aug 2, 2026
2bd6195
Merge pull request #443 from UTDallasEPICS/teacher-verification
v-patel Aug 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions .github/workflows/buildCheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,23 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 10

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache: 'pnpm'

- name: Install Dependencies from Package-Lock
run: npm ci
- name: Install Dependencies
run: pnpm install --frozen-lockfile

- name: Generate Prisma Client
run: npx prisma generate
run: pnpm prisma generate

- name: Build Project
run: npm run build
run: pnpm build

7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@ prisma/generated

prisma/dev.db
prisma/dev.db-journal

# Dev seed file
prisma/seed.ts

# non-pnpm lock files
yarn.lock
package-lock.json
43 changes: 43 additions & 0 deletions app/components/ClassContextSelect.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.class-context-select {
display: flex;
align-items: center;
gap: 0.75rem;
}

label {
color: #64748b;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}

select {
min-width: 15rem;
padding: 0.7rem 0.9rem;
background: #f8fafc;
border: 1px solid #cbd5e1;
border-radius: 0.65rem;
color: #1e293b;
cursor: pointer;
font: inherit;
font-size: 0.9rem;
font-weight: 700;
}

select:focus {
border-color: #4f46e5;
outline: none;
box-shadow: 0 0 0 3px rgb(79 70 229 / 12%);
}

@media (max-width: 800px) {
.class-context-select {
width: 100%;
}

select {
min-width: 0;
flex: 1;
}
}
162 changes: 162 additions & 0 deletions app/components/ClassContextSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<script setup lang="ts">
import { authClient } from '../utils/auth-client'

type ClassOption = {
joinToken: string
name: string
}

const route = useRoute()
const router = useRouter()
const selectedClassToken = useCookie<string | null>('selected-class-token', {
sameSite: 'lax',
})
const { data: session } = await authClient.useSession(useFetch)

const { data: classes, status: classesStatus } = await useFetch<ClassOption[]>(
'/api/admin/classes',
{
key: `admin-classes-${session.value?.user?.id ?? 'anonymous'}`,
default: () => [],
}
)

function getRouteClassToken() {
const classQuery = route.query.class
return Array.isArray(classQuery) ? classQuery[0] : classQuery
}

function getCurrentContext() {
if (route.path === '/coach/create-class') {
return 'create-class'
}

if (route.path.startsWith('/coach')) {
const classToken = getRouteClassToken() || selectedClassToken.value
return classToken ? `class:${classToken}` : 'admin'
}

return 'admin'
}

const selectedContext = ref(getCurrentContext())
const isRedirecting = ref(false)
const canAccessUniversalAdmin = computed(() => session.value?.user?.role === 'admin')
const canManageClasses = computed(
() => session.value?.user?.role === 'admin' || session.value?.user?.role === 'coach'
)

watch(
() => [route.path, route.query.class, selectedClassToken.value],
() => {
selectedContext.value = getCurrentContext()
}
)

watch(
[
classesStatus,
() => classes.value.length,
() => route.path,
() => route.query.class,
() => selectedClassToken.value,
canAccessUniversalAdmin,
canManageClasses,
],
async () => {
if (!canManageClasses.value || isRedirecting.value) {
return
}

isRedirecting.value = true

try {
if (classesStatus.value !== 'success') {
return
}

if (
!canAccessUniversalAdmin.value &&
classes.value.length === 0 &&
route.path !== '/coach/create-class'
) {
selectedClassToken.value = null
await router.replace('/coach/create-class')
return
}

if (route.path.startsWith('/coach') && route.path !== '/coach/create-class') {
const classToken = getRouteClassToken() || selectedClassToken.value
const classExists = classes.value.some((classroom) => classroom.joinToken === classToken)

if (!classToken || !classExists) {
selectedClassToken.value = null

if (canAccessUniversalAdmin.value) {
await router.replace('/admin')
return
}

const firstClass = classes.value[0]

if (firstClass) {
selectedClassToken.value = firstClass.joinToken
await router.replace({
path: '/coach',
query: { class: firstClass.joinToken },
})
}
}
}
} finally {
isRedirecting.value = false
}
},
{ immediate: true }
)

async function changeContext() {
if (selectedContext.value === 'admin') {
if (!canAccessUniversalAdmin.value) {
selectedContext.value = getCurrentContext()
return
}

selectedClassToken.value = null
await router.push('/admin')
return
}

if (selectedContext.value === 'create-class') {
await router.push('/coach/create-class')
return
}

const classToken = selectedContext.value.replace(/^class:/, '')
selectedClassToken.value = classToken
await router.push({
path: '/coach',
query: { class: classToken },
})
}
</script>

<template>
<div class="class-context-select">
<label for="class-context">Managing</label>

<select id="class-context" v-model="selectedContext" @change="changeContext">
<option v-if="canAccessUniversalAdmin" value="admin">Admin</option>

<option v-if="classesStatus === 'pending'" disabled>Loading classes…</option>

<option v-for="classroom in classes" :key="classroom.joinToken" :value="`class:${classroom.joinToken}`">
{{ classroom.name }}
</option>

<option value="create-class">+ Create New Class</option>
</select>
</div>
</template>

<style scoped src="./ClassContextSelect.css"></style>
26 changes: 26 additions & 0 deletions app/components/LogoutButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
import { authClient } from '~/utils/auth-client'

const isLoggingOut = ref(false)

async function logout() {
const confirmed = confirm('Are you sure you want to log out?')
if (!confirmed || isLoggingOut.value) return

isLoggingOut.value = true

try {
await authClient.signOut()
window.location.href = '/'
} catch (error) {
isLoggingOut.value = false
console.error('Logout failed:', error)
}
}
</script>

<template>
<button type="button" :disabled="isLoggingOut" @click="logout">
<slot>⎋ Logout</slot>
</button>
</template>
Loading
Loading