diff --git a/backend/src/database/activity.go b/backend/src/database/activity.go index 08922ef5f..9003a9efb 100644 --- a/backend/src/database/activity.go +++ b/backend/src/database/activity.go @@ -15,7 +15,7 @@ func (db *DB) GetDailyActivityByUserID(userID int, startDate time.Time, endDate if err := db.Where("user_id = ? AND created_at BETWEEN ? AND ?", userID, startDate, endDate).Find(&activities).Error; err != nil { return nil, newGetRecordsDBError(err, "activities") } - // Combine activities based on date + // Combine activities based on dat asdfae dailyActivities := make(map[time.Time]models.DailyActivity, days) for _, activity := range activities { date := activity.CreatedAt.Truncate(24 * time.Hour) diff --git a/backend/src/handlers/proxy_handler.go b/backend/src/handlers/proxy_handler.go index 7f334e709..7cb6b9ccb 100644 --- a/backend/src/handlers/proxy_handler.go +++ b/backend/src/handlers/proxy_handler.go @@ -62,17 +62,17 @@ func (srv *Server) handleForwardKiwixProxy(w http.ResponseWriter, r *http.Reques scheme = "http" } proxy := httputil.ReverseProxy{ - Director: func(req *http.Request) { - req.URL.Scheme = scheme - req.URL.Host = parsedURL.Host - req.Host = parsedURL.Host + Rewrite: func(pr *httputil.ProxyRequest) { + pr.Out.URL.Scheme = scheme + pr.Out.URL.Host = parsedURL.Host + pr.Out.Host = parsedURL.Host decodedPath, _ := url.QueryUnescape(finalEncodedPath) - req.URL.Path = decodedPath - req.URL.RawPath = finalEncodedPath - req.URL.RawQuery = r.URL.RawQuery - req.Header.Set("X-Real-IP", r.RemoteAddr) - req.Header.Set("X-Forwarded-For", r.RemoteAddr) - req.Header.Set("X-Forwarded-Proto", scheme) + pr.Out.URL.Path = decodedPath + pr.Out.URL.RawPath = finalEncodedPath + pr.Out.URL.RawQuery = r.URL.RawQuery + pr.Out.Header.Set("X-Real-IP", r.RemoteAddr) + pr.Out.Header.Set("X-Forwarded-For", r.RemoteAddr) + pr.Out.Header.Set("X-Forwarded-Proto", scheme) }, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, diff --git a/frontend/src/api/learningRecord.ts b/frontend/src/api/learningRecord.ts index bb552db0e..4fb09caca 100644 --- a/frontend/src/api/learningRecord.ts +++ b/frontend/src/api/learningRecord.ts @@ -147,9 +147,13 @@ function extractArray( /** * Facilities for the achievement location dropdown are served by a * learning-record route (`GET /api/learning-record/facilities`) because the main - * facilities endpoints are admin-only. Fetched via SWR in - * `useLearningRecordFacilities` so a failed load is never cached as an empty - * list — see that hook for why that distinction matters. + * facilities endpoints are admin-only. + * + * RTS---FIXME TEMPORARY (pilot): that route is not called right now — + * `useLearningRecordFacilities` returns a hardcoded list of the Maine women's + * facilities, since the pilot is SMWRC-only and the tool can't yet tell men's + * facilities from women's. The route stays in place; see that hook to restore + * the fetch. */ export async function apiGetEntries(): Promise<{ diff --git a/frontend/src/pages/student/digital-transcript/DigitalTranscriptWysiwygEntry.tsx b/frontend/src/pages/student/digital-transcript/DigitalTranscriptWysiwygEntry.tsx index 5ea6164fd..757a912b7 100644 --- a/frontend/src/pages/student/digital-transcript/DigitalTranscriptWysiwygEntry.tsx +++ b/frontend/src/pages/student/digital-transcript/DigitalTranscriptWysiwygEntry.tsx @@ -8,7 +8,7 @@ import { } from 'react'; import { Plus } from 'lucide-react'; import { useSearchParams } from 'react-router-dom'; -import { useAuth } from '@/auth/useAuth'; +// import { useAuth } from '@/auth/useAuth'; import { ConfirmDialog } from '@/components/shared'; import { LearningRecordPrivacyNotice } from '@/components/learning-record/LearningRecordPrivacyNotice'; import { Badge } from '@/components/ui/badge'; @@ -54,6 +54,7 @@ import { TOP_SKILLS_MAX } from './transcriptReflectionConfig'; import { LearningRecordTracker } from './learningRecordAnalytics'; +import { PILOT_DEFAULT_FACILITY } from './useLearningRecordFacilities'; /** Maps a TranscriptEntry patch key to its corresponding preview field id. */ function patchKeyToPreviewField(key: keyof TranscriptEntry): string | null { @@ -252,14 +253,22 @@ export function DigitalTranscriptWysiwygEntry({ funnelDownload }: DigitalTranscriptWysiwygEntryProps) { const isFunnel = formVariant === 'funnel'; - const { user } = useAuth(); - const defaultFacility = useMemo( - () => - user?.facility_id - ? { id: user.facility_id, name: user.facility?.name ?? '' } - : null, - [user?.facility_id, user?.facility?.name] - ); + /* + * TEMPORARY (pilot): new achievements default to SMWRC, because the pilot + * runs there and the location dropdown lists only the Maine women's + * facilities. Restore the resident's-own-facility derivation (and the + * `useAuth` import) once the list is filtered server-side: + * + * const { user } = useAuth(); + * const defaultFacility = useMemo( + * () => + * user?.facility_id + * ? { id: user.facility_id, name: user.facility?.name ?? '' } + * : null, + * [user?.facility_id, user?.facility?.name] + * ); + */ + const defaultFacility: DefaultFacility = PILOT_DEFAULT_FACILITY; const [searchParams, setSearchParams] = useSearchParams(); const [session, setSession] = useState(null); const [saveErrorRowId, setSaveErrorRowId] = useState(null); diff --git a/frontend/src/pages/student/digital-transcript/useLearningRecordFacilities.ts b/frontend/src/pages/student/digital-transcript/useLearningRecordFacilities.ts index 91d1c9c41..ae12e7186 100644 --- a/frontend/src/pages/student/digital-transcript/useLearningRecordFacilities.ts +++ b/frontend/src/pages/student/digital-transcript/useLearningRecordFacilities.ts @@ -1,29 +1,82 @@ -import useSWR from 'swr'; +// import useSWR from 'swr'; import type { LearningRecordFacility } from '@/api/learningRecord'; -import type { ServerResponseMany } from '@/types/server'; +// import type { ServerResponseMany } from '@/types/server'; /** - * Facilities for the achievement location dropdown. SWR dedupes the request - * across every mounted form row, so this fetches once rather than once per row. + * TEMPORARY (pilot): the Maine women's facilities, hardcoded. * - * A failed load must never be cached as an empty list: residents would silently - * lose the real facility list for the rest of the session and could only pick - * "Other", turning a structured facility_id into free text. SWR keeps `data` - * undefined on error, so the next mount refetches. + * The pilot is specific to SMWRC, which houses only women, so the dropdown must + * not offer every facility in the tool — men's facilities are not valid answers + * here. Until the tool can distinguish men's from women's facilities, these are + * the only two listed; anything else goes under "Other (not listed)". + * + * The ids are the real facility ids, so entries still save a structured + * facility_id and the backend join keeps working unchanged. + * + * The `GET /api/learning-record/facilities` route stays in place and is only + * commented out below — restore the SWR fetch to undo this. + */ +const SMWRC: LearningRecordFacility = { id: 2, name: 'SMWRC' }; +const WOMENS_CENTER_MCC: LearningRecordFacility = { + id: 3, + name: "Women's Center (MCC)" +}; + +const PILOT_FACILITIES: LearningRecordFacility[] = [SMWRC, WOMENS_CENTER_MCC]; + +/** + * TEMPORARY (pilot): prefilled as the location on new achievements, since the + * pilot runs at SMWRC. Replaces the resident's own facility as the default — + * see the commented-out derivation in DigitalTranscriptWysiwygEntry. + */ +export const PILOT_DEFAULT_FACILITY: LearningRecordFacility = SMWRC; + +/** + * Facilities for the achievement location dropdown. + * + * While the pilot list is hardcoded there is nothing to load and nothing that + * can fail, so `loaded` is always true and `failed` always false. The shape is + * unchanged from the fetching version so restoring the fetch touches only this + * file. */ export function useLearningRecordFacilities(): { facilities: LearningRecordFacility[]; loaded: boolean; failed: boolean; } { - const { data, error, isLoading } = useSWR< - ServerResponseMany, - Error - >('/api/learning-record/facilities'); - return { - facilities: data?.data ?? [], - loaded: !isLoading, - failed: !!error + facilities: PILOT_FACILITIES, + loaded: true, + failed: false }; } + +/* + * Pre-pilot implementation — restore this (and the imports above) once the + * facility list can be filtered server-side. + * + * SWR dedupes the request across every mounted form row, so this fetches once + * rather than once per row. + * + * A failed load must never be cached as an empty list: residents would silently + * lose the real facility list for the rest of the session and could only pick + * "Other", turning a structured facility_id into free text. SWR keeps `data` + * undefined on error, so the next mount refetches. + * + * export function useLearningRecordFacilities(): { + * facilities: LearningRecordFacility[]; + * loaded: boolean; + * failed: boolean; + * } { + * const { data, error, isLoading } = useSWR< + * ServerResponseMany, + * Error + * >('/api/learning-record/facilities'); + * + * return { + * facilities: data?.data ?? [], + * loaded: !isLoading, + * failed: !!error + * }; + * } + */