11import { usePreferences } from "@/entrypoints/degree-audit/providers/preferences-provider" ;
2- import { calculateWeightedDegreeCompletion } from "@/lib/audit-calculations" ;
2+ import {
3+ calculateWeightedDegreeCompletion ,
4+ getDuplicateCourseRequirementFlags ,
5+ getCompositeAuditRequirements ,
6+ } from "@/lib/audit-calculations" ;
37import {
48 addPlannedCourse as addPlannedCourseToStorage ,
59 getAuditData ,
610 getAuditHistory ,
711 removePlannedCourse as removePlannedCourseFromStorage ,
12+ renameAudit ,
813 wipeAllPlannedCourses as wipeAllPlannedCoursesFromStorage ,
914} from "@/lib/backend/storage" ;
1015import {
1116 AuditHistoryData ,
1217 AuditRequirement ,
18+ CompositeAuditData ,
19+ CompositeAuditRequirement ,
1320 Course ,
1421 CourseId ,
1522 CurrentAuditProgress ,
1623 DegreeAuditCardProps ,
24+ DuplicateCourseRequirementFlag ,
1725 PlannedCourseOutline ,
1826 RequirementRule ,
1927 StringSemester ,
@@ -35,10 +43,14 @@ type AuditRequirementLike = Omit<AuditRequirement, "rule"> & {
3543interface AuditContextType {
3644 sections : AuditRequirement [ ] ;
3745 courses : Course [ ] ;
46+ compositeAuditData : CompositeAuditData ;
47+ compositeRequirements : CompositeAuditRequirement [ ] ;
48+ duplicateCourseFlags : DuplicateCourseRequirementFlag [ ] ;
3849 history : AuditHistoryData ;
3950 currentAuditId : string ;
4051 currentAudit : DegreeAuditCardProps ;
4152 setCurrentAuditId : ( id : string ) => void ;
53+ renameAuditTitle : ( auditId : string , title : string ) => Promise < boolean > ;
4254 progresses : CurrentAuditProgress ;
4355 semesters : SemesterInfo ;
4456 getCourseById : ( id : CourseId ) => Course ;
@@ -98,6 +110,13 @@ function normalizeRequirements(
98110 } ) ) ;
99111}
100112
113+ function getAuditDisplayName (
114+ audit : DegreeAuditCardProps | undefined ,
115+ auditId : string ,
116+ ) {
117+ return audit ?. title ?? audit ?. majors ?. join ( "; " ) ?? auditId ;
118+ }
119+
101120export const AuditContextProvider = ( {
102121 children,
103122} : {
@@ -111,8 +130,19 @@ export const AuditContextProvider = ({
111130
112131 const [ courseDict , setCourseDict ] = useState < Record < CourseId , Course > > ( { } ) ;
113132 const [ sections , setSections ] = useState < AuditRequirement [ ] > ( [ ] ) ;
133+ const [ compositeAuditData , setCompositeAuditData ] =
134+ useState < CompositeAuditData > ( { audits : [ ] } ) ;
114135 const [ history , setHistory ] = useState < AuditHistoryData > ( ) ;
115136
137+ const compositeRequirements = useMemo (
138+ ( ) => getCompositeAuditRequirements ( compositeAuditData ) ,
139+ [ compositeAuditData ] ,
140+ ) ;
141+ const duplicateCourseFlags = useMemo (
142+ ( ) => getDuplicateCourseRequirementFlags ( compositeAuditData ) ,
143+ [ compositeAuditData ] ,
144+ ) ;
145+
116146 const progresses = useMemo (
117147 ( ) => calculateWeightedDegreeCompletion ( sections ?? [ ] , courseDict ) ,
118148 [ sections , courseDict ] ,
@@ -230,6 +260,25 @@ export const AuditContextProvider = ({
230260 return numRemoved ;
231261 }
232262
263+ // Keep the provider state in sync after storage accepts the rename.
264+ async function renameAuditTitle ( auditId : string , title : string ) {
265+ const cleanTitle = title . trim ( ) ;
266+ if ( ! cleanTitle ) {
267+ return false ;
268+ }
269+
270+ const updatedHistory = await renameAudit ( auditId , cleanTitle ) ;
271+ if ( ! updatedHistory ) {
272+ return false ;
273+ }
274+
275+ setHistory ( updatedHistory ) ;
276+ setCompositeAuditData ( ( prev ) => ( {
277+ audits : prev . audits . map ( ( audit ) => ( { ...audit , name : cleanTitle } ) ) ,
278+ } ) ) ;
279+ return true ;
280+ }
281+
233282 const currentAudit = useMemo ( ( ) => {
234283 return history ?. audits . find ( ( a ) => a . auditId === currentAuditId ) ?? { } ;
235284 } , [ history , currentAuditId ] ) ;
@@ -267,19 +316,21 @@ export const AuditContextProvider = ({
267316 // Load requirements from cache
268317 const cached = await getAuditData ( currentAuditId ! ) ;
269318 if ( cached ) {
270- setSections (
271- cached . requirements . map ( ( section ) => ( {
272- ...section ,
273- rules : section . rules . map ( ( rule ) => ( {
274- ...rule ,
275- progressUnit : rule . progressUnit ?? "hours" ,
276- courses : rule . courses ,
277- } ) ) ,
278- } ) ) ,
279- ) ;
319+ const namedAudit = {
320+ ...cached ,
321+ name : getAuditDisplayName ( matchingAudit , currentAuditId ! ) ,
322+ } ;
323+ const composite = { audits : [ namedAudit ] } ;
324+ setCompositeAuditData ( composite ) ;
325+ setSections ( getCompositeAuditRequirements ( composite ) ) ;
280326 console . log ( "[Main] courses" , cached . courses ) ;
281327 setCourseDict ( cached . courses ) ;
282- } else console . warn ( `[Main] Audit ${ currentAuditId } not in cache.` ) ;
328+ } else {
329+ setCompositeAuditData ( { audits : [ ] } ) ;
330+ setSections ( [ ] ) ;
331+ setCourseDict ( { } ) ;
332+ console . warn ( `[Main] Audit ${ currentAuditId } not in cache.` ) ;
333+ }
283334
284335 setLoaded ( true ) ;
285336 } catch ( error ) {
@@ -300,6 +351,9 @@ export const AuditContextProvider = ({
300351 value = { {
301352 sections,
302353 courses : Object . values ( courseDict ) ,
354+ compositeAuditData,
355+ compositeRequirements,
356+ duplicateCourseFlags,
303357 history : history ! ,
304358 semesters,
305359 currentAuditId,
@@ -309,6 +363,7 @@ export const AuditContextProvider = ({
309363 setCurrentAuditId ( id ) ;
310364 updateLastAuditId ( id ) ;
311365 } ,
366+ renameAuditTitle,
312367 moveCourseToNewSemester,
313368 progresses,
314369 getCourseById : ( id ) => {
0 commit comments