@thanh01.pmt/interactive-quiz-kit 1.0.47 → 1.0.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3998,20 +3998,30 @@ var PracticeHistoryService = class {
3998
3998
  static saveHistory(history2) {
3999
3999
  if (typeof window !== "undefined") {
4000
4000
  try {
4001
- localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(history2));
4001
+ localStorage.setItem(
4002
+ LOCAL_STORAGE_KEY,
4003
+ JSON.stringify(history2)
4004
+ );
4002
4005
  } catch (e2) {
4003
- console.error("Error saving practice history to localStorage:", e2);
4006
+ console.error(
4007
+ "Error saving practice history to localStorage:",
4008
+ e2
4009
+ );
4004
4010
  }
4005
4011
  }
4006
4012
  }
4007
4013
  static saveCompletedPracticeSession(quizConfig, result, review = null) {
4008
4014
  const history2 = this.getPracticeHistory();
4009
4015
  const topicsCovered = Array.from(
4010
- new Set(quizConfig.questions.map((q) => JSON.stringify({
4011
- subject: q.subject || "Uncategorized",
4012
- category: q.category || "General",
4013
- topic: q.topic || "General Topic"
4014
- })))
4016
+ new Set(
4017
+ quizConfig.questions.map(
4018
+ (q) => JSON.stringify({
4019
+ subject: q.subject || "Uncategorized",
4020
+ category: q.category || "General",
4021
+ topic: q.topic || "General Topic"
4022
+ })
4023
+ )
4024
+ )
4015
4025
  ).map((s2) => JSON.parse(s2));
4016
4026
  const newSession = {
4017
4027
  id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -4029,7 +4039,9 @@ var PracticeHistoryService = class {
4029
4039
  history2.unshift(newSession);
4030
4040
  this.saveHistory(history2);
4031
4041
  if (this.syncProvider) {
4032
- console.log(`Sync provider found. Pushing session ${newSession.id} to backend.`);
4042
+ console.log(
4043
+ `Sync provider found. Pushing session ${newSession.id} to backend.`
4044
+ );
4033
4045
  this.syncProvider.pushSession(newSession).catch((err) => {
4034
4046
  console.error("Sync provider failed to push session:", err);
4035
4047
  });
@@ -4043,7 +4055,9 @@ var PracticeHistoryService = class {
4043
4055
  history2[sessionIndex].quizReview = review;
4044
4056
  this.saveHistory(history2);
4045
4057
  } else {
4046
- console.warn(`Could not find session with ID "${sessionId}" to update review.`);
4058
+ console.warn(
4059
+ `Could not find session with ID "${sessionId}" to update review.`
4060
+ );
4047
4061
  }
4048
4062
  }
4049
4063
  static getPracticeSessionById(sessionId) {
@@ -4063,6 +4077,14 @@ var PracticeHistoryService = class {
4063
4077
  }
4064
4078
  static getPracticeStats() {
4065
4079
  const history2 = this.getPracticeHistory();
4080
+ return this.calculateStatsFromHistory(history2);
4081
+ }
4082
+ static clearHistory() {
4083
+ if (typeof window !== "undefined") {
4084
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
4085
+ }
4086
+ }
4087
+ static calculateStatsFromHistory(history2) {
4066
4088
  if (history2.length === 0) {
4067
4089
  return {
4068
4090
  totalSessions: 0,
@@ -4101,7 +4123,9 @@ var PracticeHistoryService = class {
4101
4123
  const currentDay = new Date(sortedDays[i]);
4102
4124
  const nextDay = new Date(sortedDays[i + 1]);
4103
4125
  const diffTime = currentDay.getTime() - nextDay.getTime();
4104
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
4126
+ const diffDays = Math.round(
4127
+ diffTime / (1e3 * 60 * 60 * 24)
4128
+ );
4105
4129
  if (diffDays === 1) {
4106
4130
  currentStreak++;
4107
4131
  } else {
@@ -4133,22 +4157,31 @@ var PracticeHistoryService = class {
4133
4157
  const subjectPerf = {};
4134
4158
  const topicPerf = {};
4135
4159
  history2.forEach((session) => {
4136
- session.summary.topics.forEach((topicInfo) => {
4137
- if (session.summary.percentage !== null) {
4138
- if (!subjectPerf[topicInfo.subject]) subjectPerf[topicInfo.subject] = { total: 0, count: 0 };
4139
- subjectPerf[topicInfo.subject].total += session.summary.percentage;
4140
- subjectPerf[topicInfo.subject].count++;
4141
- if (!topicPerf[topicInfo.topic]) topicPerf[topicInfo.topic] = { total: 0, count: 0 };
4142
- topicPerf[topicInfo.topic].total += session.summary.percentage;
4143
- topicPerf[topicInfo.topic].count++;
4160
+ session.summary.topics.forEach(
4161
+ (topicInfo) => {
4162
+ if (session.summary.percentage !== null) {
4163
+ if (!subjectPerf[topicInfo.subject])
4164
+ subjectPerf[topicInfo.subject] = {
4165
+ total: 0,
4166
+ count: 0
4167
+ };
4168
+ subjectPerf[topicInfo.subject].total += session.summary.percentage;
4169
+ subjectPerf[topicInfo.subject].count++;
4170
+ if (!topicPerf[topicInfo.topic])
4171
+ topicPerf[topicInfo.topic] = { total: 0, count: 0 };
4172
+ topicPerf[topicInfo.topic].total += session.summary.percentage;
4173
+ topicPerf[topicInfo.topic].count++;
4174
+ }
4144
4175
  }
4145
- });
4176
+ );
4146
4177
  });
4147
4178
  const formatPerf = (perfData) => {
4148
4179
  return Object.entries(perfData).map(([name3, data]) => ({
4149
4180
  name: name3,
4150
4181
  totalSessions: data.count,
4151
- averageScore: parseFloat((data.total / data.count).toFixed(2))
4182
+ averageScore: parseFloat(
4183
+ (data.total / data.count).toFixed(2)
4184
+ )
4152
4185
  })).sort((a2, b) => b.totalSessions - a2.totalSessions);
4153
4186
  };
4154
4187
  return {
@@ -4160,11 +4193,6 @@ var PracticeHistoryService = class {
4160
4193
  performanceByTopic: formatPerf(topicPerf)
4161
4194
  };
4162
4195
  }
4163
- static clearHistory() {
4164
- if (typeof window !== "undefined") {
4165
- localStorage.removeItem(LOCAL_STORAGE_KEY);
4166
- }
4167
- }
4168
4196
  };
4169
4197
  // NEW: A static property to hold the injected sync provider
4170
4198
  PracticeHistoryService.syncProvider = null;
@@ -3972,20 +3972,30 @@ var PracticeHistoryService = class {
3972
3972
  static saveHistory(history2) {
3973
3973
  if (typeof window !== "undefined") {
3974
3974
  try {
3975
- localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(history2));
3975
+ localStorage.setItem(
3976
+ LOCAL_STORAGE_KEY,
3977
+ JSON.stringify(history2)
3978
+ );
3976
3979
  } catch (e2) {
3977
- console.error("Error saving practice history to localStorage:", e2);
3980
+ console.error(
3981
+ "Error saving practice history to localStorage:",
3982
+ e2
3983
+ );
3978
3984
  }
3979
3985
  }
3980
3986
  }
3981
3987
  static saveCompletedPracticeSession(quizConfig, result, review = null) {
3982
3988
  const history2 = this.getPracticeHistory();
3983
3989
  const topicsCovered = Array.from(
3984
- new Set(quizConfig.questions.map((q) => JSON.stringify({
3985
- subject: q.subject || "Uncategorized",
3986
- category: q.category || "General",
3987
- topic: q.topic || "General Topic"
3988
- })))
3990
+ new Set(
3991
+ quizConfig.questions.map(
3992
+ (q) => JSON.stringify({
3993
+ subject: q.subject || "Uncategorized",
3994
+ category: q.category || "General",
3995
+ topic: q.topic || "General Topic"
3996
+ })
3997
+ )
3998
+ )
3989
3999
  ).map((s2) => JSON.parse(s2));
3990
4000
  const newSession = {
3991
4001
  id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -4003,7 +4013,9 @@ var PracticeHistoryService = class {
4003
4013
  history2.unshift(newSession);
4004
4014
  this.saveHistory(history2);
4005
4015
  if (this.syncProvider) {
4006
- console.log(`Sync provider found. Pushing session ${newSession.id} to backend.`);
4016
+ console.log(
4017
+ `Sync provider found. Pushing session ${newSession.id} to backend.`
4018
+ );
4007
4019
  this.syncProvider.pushSession(newSession).catch((err) => {
4008
4020
  console.error("Sync provider failed to push session:", err);
4009
4021
  });
@@ -4017,7 +4029,9 @@ var PracticeHistoryService = class {
4017
4029
  history2[sessionIndex].quizReview = review;
4018
4030
  this.saveHistory(history2);
4019
4031
  } else {
4020
- console.warn(`Could not find session with ID "${sessionId}" to update review.`);
4032
+ console.warn(
4033
+ `Could not find session with ID "${sessionId}" to update review.`
4034
+ );
4021
4035
  }
4022
4036
  }
4023
4037
  static getPracticeSessionById(sessionId) {
@@ -4037,6 +4051,14 @@ var PracticeHistoryService = class {
4037
4051
  }
4038
4052
  static getPracticeStats() {
4039
4053
  const history2 = this.getPracticeHistory();
4054
+ return this.calculateStatsFromHistory(history2);
4055
+ }
4056
+ static clearHistory() {
4057
+ if (typeof window !== "undefined") {
4058
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
4059
+ }
4060
+ }
4061
+ static calculateStatsFromHistory(history2) {
4040
4062
  if (history2.length === 0) {
4041
4063
  return {
4042
4064
  totalSessions: 0,
@@ -4075,7 +4097,9 @@ var PracticeHistoryService = class {
4075
4097
  const currentDay = new Date(sortedDays[i]);
4076
4098
  const nextDay = new Date(sortedDays[i + 1]);
4077
4099
  const diffTime = currentDay.getTime() - nextDay.getTime();
4078
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
4100
+ const diffDays = Math.round(
4101
+ diffTime / (1e3 * 60 * 60 * 24)
4102
+ );
4079
4103
  if (diffDays === 1) {
4080
4104
  currentStreak++;
4081
4105
  } else {
@@ -4107,22 +4131,31 @@ var PracticeHistoryService = class {
4107
4131
  const subjectPerf = {};
4108
4132
  const topicPerf = {};
4109
4133
  history2.forEach((session) => {
4110
- session.summary.topics.forEach((topicInfo) => {
4111
- if (session.summary.percentage !== null) {
4112
- if (!subjectPerf[topicInfo.subject]) subjectPerf[topicInfo.subject] = { total: 0, count: 0 };
4113
- subjectPerf[topicInfo.subject].total += session.summary.percentage;
4114
- subjectPerf[topicInfo.subject].count++;
4115
- if (!topicPerf[topicInfo.topic]) topicPerf[topicInfo.topic] = { total: 0, count: 0 };
4116
- topicPerf[topicInfo.topic].total += session.summary.percentage;
4117
- topicPerf[topicInfo.topic].count++;
4134
+ session.summary.topics.forEach(
4135
+ (topicInfo) => {
4136
+ if (session.summary.percentage !== null) {
4137
+ if (!subjectPerf[topicInfo.subject])
4138
+ subjectPerf[topicInfo.subject] = {
4139
+ total: 0,
4140
+ count: 0
4141
+ };
4142
+ subjectPerf[topicInfo.subject].total += session.summary.percentage;
4143
+ subjectPerf[topicInfo.subject].count++;
4144
+ if (!topicPerf[topicInfo.topic])
4145
+ topicPerf[topicInfo.topic] = { total: 0, count: 0 };
4146
+ topicPerf[topicInfo.topic].total += session.summary.percentage;
4147
+ topicPerf[topicInfo.topic].count++;
4148
+ }
4118
4149
  }
4119
- });
4150
+ );
4120
4151
  });
4121
4152
  const formatPerf = (perfData) => {
4122
4153
  return Object.entries(perfData).map(([name3, data]) => ({
4123
4154
  name: name3,
4124
4155
  totalSessions: data.count,
4125
- averageScore: parseFloat((data.total / data.count).toFixed(2))
4156
+ averageScore: parseFloat(
4157
+ (data.total / data.count).toFixed(2)
4158
+ )
4126
4159
  })).sort((a2, b) => b.totalSessions - a2.totalSessions);
4127
4160
  };
4128
4161
  return {
@@ -4134,11 +4167,6 @@ var PracticeHistoryService = class {
4134
4167
  performanceByTopic: formatPerf(topicPerf)
4135
4168
  };
4136
4169
  }
4137
- static clearHistory() {
4138
- if (typeof window !== "undefined") {
4139
- localStorage.removeItem(LOCAL_STORAGE_KEY);
4140
- }
4141
- }
4142
4170
  };
4143
4171
  // NEW: A static property to hold the injected sync provider
4144
4172
  PracticeHistoryService.syncProvider = null;
package/dist/index.cjs CHANGED
@@ -1964,20 +1964,30 @@ var PracticeHistoryService = class {
1964
1964
  static saveHistory(history) {
1965
1965
  if (typeof window !== "undefined") {
1966
1966
  try {
1967
- localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(history));
1967
+ localStorage.setItem(
1968
+ LOCAL_STORAGE_KEY,
1969
+ JSON.stringify(history)
1970
+ );
1968
1971
  } catch (e) {
1969
- console.error("Error saving practice history to localStorage:", e);
1972
+ console.error(
1973
+ "Error saving practice history to localStorage:",
1974
+ e
1975
+ );
1970
1976
  }
1971
1977
  }
1972
1978
  }
1973
1979
  static saveCompletedPracticeSession(quizConfig, result, review = null) {
1974
1980
  const history = this.getPracticeHistory();
1975
1981
  const topicsCovered = Array.from(
1976
- new Set(quizConfig.questions.map((q) => JSON.stringify({
1977
- subject: q.subject || "Uncategorized",
1978
- category: q.category || "General",
1979
- topic: q.topic || "General Topic"
1980
- })))
1982
+ new Set(
1983
+ quizConfig.questions.map(
1984
+ (q) => JSON.stringify({
1985
+ subject: q.subject || "Uncategorized",
1986
+ category: q.category || "General",
1987
+ topic: q.topic || "General Topic"
1988
+ })
1989
+ )
1990
+ )
1981
1991
  ).map((s) => JSON.parse(s));
1982
1992
  const newSession = {
1983
1993
  id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -1995,7 +2005,9 @@ var PracticeHistoryService = class {
1995
2005
  history.unshift(newSession);
1996
2006
  this.saveHistory(history);
1997
2007
  if (this.syncProvider) {
1998
- console.log(`Sync provider found. Pushing session ${newSession.id} to backend.`);
2008
+ console.log(
2009
+ `Sync provider found. Pushing session ${newSession.id} to backend.`
2010
+ );
1999
2011
  this.syncProvider.pushSession(newSession).catch((err) => {
2000
2012
  console.error("Sync provider failed to push session:", err);
2001
2013
  });
@@ -2009,7 +2021,9 @@ var PracticeHistoryService = class {
2009
2021
  history[sessionIndex].quizReview = review;
2010
2022
  this.saveHistory(history);
2011
2023
  } else {
2012
- console.warn(`Could not find session with ID "${sessionId}" to update review.`);
2024
+ console.warn(
2025
+ `Could not find session with ID "${sessionId}" to update review.`
2026
+ );
2013
2027
  }
2014
2028
  }
2015
2029
  static getPracticeSessionById(sessionId) {
@@ -2029,6 +2043,14 @@ var PracticeHistoryService = class {
2029
2043
  }
2030
2044
  static getPracticeStats() {
2031
2045
  const history = this.getPracticeHistory();
2046
+ return this.calculateStatsFromHistory(history);
2047
+ }
2048
+ static clearHistory() {
2049
+ if (typeof window !== "undefined") {
2050
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
2051
+ }
2052
+ }
2053
+ static calculateStatsFromHistory(history) {
2032
2054
  if (history.length === 0) {
2033
2055
  return {
2034
2056
  totalSessions: 0,
@@ -2067,7 +2089,9 @@ var PracticeHistoryService = class {
2067
2089
  const currentDay = new Date(sortedDays[i]);
2068
2090
  const nextDay = new Date(sortedDays[i + 1]);
2069
2091
  const diffTime = currentDay.getTime() - nextDay.getTime();
2070
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
2092
+ const diffDays = Math.round(
2093
+ diffTime / (1e3 * 60 * 60 * 24)
2094
+ );
2071
2095
  if (diffDays === 1) {
2072
2096
  currentStreak++;
2073
2097
  } else {
@@ -2099,22 +2123,31 @@ var PracticeHistoryService = class {
2099
2123
  const subjectPerf = {};
2100
2124
  const topicPerf = {};
2101
2125
  history.forEach((session) => {
2102
- session.summary.topics.forEach((topicInfo) => {
2103
- if (session.summary.percentage !== null) {
2104
- if (!subjectPerf[topicInfo.subject]) subjectPerf[topicInfo.subject] = { total: 0, count: 0 };
2105
- subjectPerf[topicInfo.subject].total += session.summary.percentage;
2106
- subjectPerf[topicInfo.subject].count++;
2107
- if (!topicPerf[topicInfo.topic]) topicPerf[topicInfo.topic] = { total: 0, count: 0 };
2108
- topicPerf[topicInfo.topic].total += session.summary.percentage;
2109
- topicPerf[topicInfo.topic].count++;
2126
+ session.summary.topics.forEach(
2127
+ (topicInfo) => {
2128
+ if (session.summary.percentage !== null) {
2129
+ if (!subjectPerf[topicInfo.subject])
2130
+ subjectPerf[topicInfo.subject] = {
2131
+ total: 0,
2132
+ count: 0
2133
+ };
2134
+ subjectPerf[topicInfo.subject].total += session.summary.percentage;
2135
+ subjectPerf[topicInfo.subject].count++;
2136
+ if (!topicPerf[topicInfo.topic])
2137
+ topicPerf[topicInfo.topic] = { total: 0, count: 0 };
2138
+ topicPerf[topicInfo.topic].total += session.summary.percentage;
2139
+ topicPerf[topicInfo.topic].count++;
2140
+ }
2110
2141
  }
2111
- });
2142
+ );
2112
2143
  });
2113
2144
  const formatPerf = (perfData) => {
2114
2145
  return Object.entries(perfData).map(([name, data]) => ({
2115
2146
  name,
2116
2147
  totalSessions: data.count,
2117
- averageScore: parseFloat((data.total / data.count).toFixed(2))
2148
+ averageScore: parseFloat(
2149
+ (data.total / data.count).toFixed(2)
2150
+ )
2118
2151
  })).sort((a, b) => b.totalSessions - a.totalSessions);
2119
2152
  };
2120
2153
  return {
@@ -2126,11 +2159,6 @@ var PracticeHistoryService = class {
2126
2159
  performanceByTopic: formatPerf(topicPerf)
2127
2160
  };
2128
2161
  }
2129
- static clearHistory() {
2130
- if (typeof window !== "undefined") {
2131
- localStorage.removeItem(LOCAL_STORAGE_KEY);
2132
- }
2133
- }
2134
2162
  };
2135
2163
  // NEW: A static property to hold the injected sync provider
2136
2164
  PracticeHistoryService.syncProvider = null;
package/dist/index.d.cts CHANGED
@@ -246,6 +246,7 @@ declare class PracticeHistoryService {
246
246
  static getPracticeHistorySummary(): PracticeSessionSummary[];
247
247
  static getPracticeStats(): PracticeStats;
248
248
  static clearHistory(): void;
249
+ static calculateStatsFromHistory(history: PracticeSession[]): PracticeStats;
249
250
  }
250
251
 
251
252
  declare class AchievementService {
package/dist/index.d.ts CHANGED
@@ -246,6 +246,7 @@ declare class PracticeHistoryService {
246
246
  static getPracticeHistorySummary(): PracticeSessionSummary[];
247
247
  static getPracticeStats(): PracticeStats;
248
248
  static clearHistory(): void;
249
+ static calculateStatsFromHistory(history: PracticeSession[]): PracticeStats;
249
250
  }
250
251
 
251
252
  declare class AchievementService {
package/dist/index.mjs CHANGED
@@ -1958,20 +1958,30 @@ var PracticeHistoryService = class {
1958
1958
  static saveHistory(history) {
1959
1959
  if (typeof window !== "undefined") {
1960
1960
  try {
1961
- localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(history));
1961
+ localStorage.setItem(
1962
+ LOCAL_STORAGE_KEY,
1963
+ JSON.stringify(history)
1964
+ );
1962
1965
  } catch (e) {
1963
- console.error("Error saving practice history to localStorage:", e);
1966
+ console.error(
1967
+ "Error saving practice history to localStorage:",
1968
+ e
1969
+ );
1964
1970
  }
1965
1971
  }
1966
1972
  }
1967
1973
  static saveCompletedPracticeSession(quizConfig, result, review = null) {
1968
1974
  const history = this.getPracticeHistory();
1969
1975
  const topicsCovered = Array.from(
1970
- new Set(quizConfig.questions.map((q) => JSON.stringify({
1971
- subject: q.subject || "Uncategorized",
1972
- category: q.category || "General",
1973
- topic: q.topic || "General Topic"
1974
- })))
1976
+ new Set(
1977
+ quizConfig.questions.map(
1978
+ (q) => JSON.stringify({
1979
+ subject: q.subject || "Uncategorized",
1980
+ category: q.category || "General",
1981
+ topic: q.topic || "General Topic"
1982
+ })
1983
+ )
1984
+ )
1975
1985
  ).map((s) => JSON.parse(s));
1976
1986
  const newSession = {
1977
1987
  id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -1989,7 +1999,9 @@ var PracticeHistoryService = class {
1989
1999
  history.unshift(newSession);
1990
2000
  this.saveHistory(history);
1991
2001
  if (this.syncProvider) {
1992
- console.log(`Sync provider found. Pushing session ${newSession.id} to backend.`);
2002
+ console.log(
2003
+ `Sync provider found. Pushing session ${newSession.id} to backend.`
2004
+ );
1993
2005
  this.syncProvider.pushSession(newSession).catch((err) => {
1994
2006
  console.error("Sync provider failed to push session:", err);
1995
2007
  });
@@ -2003,7 +2015,9 @@ var PracticeHistoryService = class {
2003
2015
  history[sessionIndex].quizReview = review;
2004
2016
  this.saveHistory(history);
2005
2017
  } else {
2006
- console.warn(`Could not find session with ID "${sessionId}" to update review.`);
2018
+ console.warn(
2019
+ `Could not find session with ID "${sessionId}" to update review.`
2020
+ );
2007
2021
  }
2008
2022
  }
2009
2023
  static getPracticeSessionById(sessionId) {
@@ -2023,6 +2037,14 @@ var PracticeHistoryService = class {
2023
2037
  }
2024
2038
  static getPracticeStats() {
2025
2039
  const history = this.getPracticeHistory();
2040
+ return this.calculateStatsFromHistory(history);
2041
+ }
2042
+ static clearHistory() {
2043
+ if (typeof window !== "undefined") {
2044
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
2045
+ }
2046
+ }
2047
+ static calculateStatsFromHistory(history) {
2026
2048
  if (history.length === 0) {
2027
2049
  return {
2028
2050
  totalSessions: 0,
@@ -2061,7 +2083,9 @@ var PracticeHistoryService = class {
2061
2083
  const currentDay = new Date(sortedDays[i]);
2062
2084
  const nextDay = new Date(sortedDays[i + 1]);
2063
2085
  const diffTime = currentDay.getTime() - nextDay.getTime();
2064
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
2086
+ const diffDays = Math.round(
2087
+ diffTime / (1e3 * 60 * 60 * 24)
2088
+ );
2065
2089
  if (diffDays === 1) {
2066
2090
  currentStreak++;
2067
2091
  } else {
@@ -2093,22 +2117,31 @@ var PracticeHistoryService = class {
2093
2117
  const subjectPerf = {};
2094
2118
  const topicPerf = {};
2095
2119
  history.forEach((session) => {
2096
- session.summary.topics.forEach((topicInfo) => {
2097
- if (session.summary.percentage !== null) {
2098
- if (!subjectPerf[topicInfo.subject]) subjectPerf[topicInfo.subject] = { total: 0, count: 0 };
2099
- subjectPerf[topicInfo.subject].total += session.summary.percentage;
2100
- subjectPerf[topicInfo.subject].count++;
2101
- if (!topicPerf[topicInfo.topic]) topicPerf[topicInfo.topic] = { total: 0, count: 0 };
2102
- topicPerf[topicInfo.topic].total += session.summary.percentage;
2103
- topicPerf[topicInfo.topic].count++;
2120
+ session.summary.topics.forEach(
2121
+ (topicInfo) => {
2122
+ if (session.summary.percentage !== null) {
2123
+ if (!subjectPerf[topicInfo.subject])
2124
+ subjectPerf[topicInfo.subject] = {
2125
+ total: 0,
2126
+ count: 0
2127
+ };
2128
+ subjectPerf[topicInfo.subject].total += session.summary.percentage;
2129
+ subjectPerf[topicInfo.subject].count++;
2130
+ if (!topicPerf[topicInfo.topic])
2131
+ topicPerf[topicInfo.topic] = { total: 0, count: 0 };
2132
+ topicPerf[topicInfo.topic].total += session.summary.percentage;
2133
+ topicPerf[topicInfo.topic].count++;
2134
+ }
2104
2135
  }
2105
- });
2136
+ );
2106
2137
  });
2107
2138
  const formatPerf = (perfData) => {
2108
2139
  return Object.entries(perfData).map(([name, data]) => ({
2109
2140
  name,
2110
2141
  totalSessions: data.count,
2111
- averageScore: parseFloat((data.total / data.count).toFixed(2))
2142
+ averageScore: parseFloat(
2143
+ (data.total / data.count).toFixed(2)
2144
+ )
2112
2145
  })).sort((a, b) => b.totalSessions - a.totalSessions);
2113
2146
  };
2114
2147
  return {
@@ -2120,11 +2153,6 @@ var PracticeHistoryService = class {
2120
2153
  performanceByTopic: formatPerf(topicPerf)
2121
2154
  };
2122
2155
  }
2123
- static clearHistory() {
2124
- if (typeof window !== "undefined") {
2125
- localStorage.removeItem(LOCAL_STORAGE_KEY);
2126
- }
2127
- }
2128
2156
  };
2129
2157
  // NEW: A static property to hold the injected sync provider
2130
2158
  PracticeHistoryService.syncProvider = null;
package/dist/react-ui.cjs CHANGED
@@ -139588,20 +139588,30 @@ var PracticeHistoryService = class {
139588
139588
  static saveHistory(history2) {
139589
139589
  if (typeof window !== "undefined") {
139590
139590
  try {
139591
- localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(history2));
139591
+ localStorage.setItem(
139592
+ LOCAL_STORAGE_KEY,
139593
+ JSON.stringify(history2)
139594
+ );
139592
139595
  } catch (e3) {
139593
- console.error("Error saving practice history to localStorage:", e3);
139596
+ console.error(
139597
+ "Error saving practice history to localStorage:",
139598
+ e3
139599
+ );
139594
139600
  }
139595
139601
  }
139596
139602
  }
139597
139603
  static saveCompletedPracticeSession(quizConfig, result, review = null) {
139598
139604
  const history2 = this.getPracticeHistory();
139599
139605
  const topicsCovered = Array.from(
139600
- new Set(quizConfig.questions.map((q2) => JSON.stringify({
139601
- subject: q2.subject || "Uncategorized",
139602
- category: q2.category || "General",
139603
- topic: q2.topic || "General Topic"
139604
- })))
139606
+ new Set(
139607
+ quizConfig.questions.map(
139608
+ (q2) => JSON.stringify({
139609
+ subject: q2.subject || "Uncategorized",
139610
+ category: q2.category || "General",
139611
+ topic: q2.topic || "General Topic"
139612
+ })
139613
+ )
139614
+ )
139605
139615
  ).map((s4) => JSON.parse(s4));
139606
139616
  const newSession = {
139607
139617
  id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -139619,7 +139629,9 @@ var PracticeHistoryService = class {
139619
139629
  history2.unshift(newSession);
139620
139630
  this.saveHistory(history2);
139621
139631
  if (this.syncProvider) {
139622
- console.log(`Sync provider found. Pushing session ${newSession.id} to backend.`);
139632
+ console.log(
139633
+ `Sync provider found. Pushing session ${newSession.id} to backend.`
139634
+ );
139623
139635
  this.syncProvider.pushSession(newSession).catch((err) => {
139624
139636
  console.error("Sync provider failed to push session:", err);
139625
139637
  });
@@ -139633,7 +139645,9 @@ var PracticeHistoryService = class {
139633
139645
  history2[sessionIndex].quizReview = review;
139634
139646
  this.saveHistory(history2);
139635
139647
  } else {
139636
- console.warn(`Could not find session with ID "${sessionId}" to update review.`);
139648
+ console.warn(
139649
+ `Could not find session with ID "${sessionId}" to update review.`
139650
+ );
139637
139651
  }
139638
139652
  }
139639
139653
  static getPracticeSessionById(sessionId) {
@@ -139653,6 +139667,14 @@ var PracticeHistoryService = class {
139653
139667
  }
139654
139668
  static getPracticeStats() {
139655
139669
  const history2 = this.getPracticeHistory();
139670
+ return this.calculateStatsFromHistory(history2);
139671
+ }
139672
+ static clearHistory() {
139673
+ if (typeof window !== "undefined") {
139674
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
139675
+ }
139676
+ }
139677
+ static calculateStatsFromHistory(history2) {
139656
139678
  if (history2.length === 0) {
139657
139679
  return {
139658
139680
  totalSessions: 0,
@@ -139691,7 +139713,9 @@ var PracticeHistoryService = class {
139691
139713
  const currentDay = new Date(sortedDays[i2]);
139692
139714
  const nextDay = new Date(sortedDays[i2 + 1]);
139693
139715
  const diffTime = currentDay.getTime() - nextDay.getTime();
139694
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
139716
+ const diffDays = Math.round(
139717
+ diffTime / (1e3 * 60 * 60 * 24)
139718
+ );
139695
139719
  if (diffDays === 1) {
139696
139720
  currentStreak++;
139697
139721
  } else {
@@ -139723,22 +139747,31 @@ var PracticeHistoryService = class {
139723
139747
  const subjectPerf = {};
139724
139748
  const topicPerf = {};
139725
139749
  history2.forEach((session) => {
139726
- session.summary.topics.forEach((topicInfo) => {
139727
- if (session.summary.percentage !== null) {
139728
- if (!subjectPerf[topicInfo.subject]) subjectPerf[topicInfo.subject] = { total: 0, count: 0 };
139729
- subjectPerf[topicInfo.subject].total += session.summary.percentage;
139730
- subjectPerf[topicInfo.subject].count++;
139731
- if (!topicPerf[topicInfo.topic]) topicPerf[topicInfo.topic] = { total: 0, count: 0 };
139732
- topicPerf[topicInfo.topic].total += session.summary.percentage;
139733
- topicPerf[topicInfo.topic].count++;
139750
+ session.summary.topics.forEach(
139751
+ (topicInfo) => {
139752
+ if (session.summary.percentage !== null) {
139753
+ if (!subjectPerf[topicInfo.subject])
139754
+ subjectPerf[topicInfo.subject] = {
139755
+ total: 0,
139756
+ count: 0
139757
+ };
139758
+ subjectPerf[topicInfo.subject].total += session.summary.percentage;
139759
+ subjectPerf[topicInfo.subject].count++;
139760
+ if (!topicPerf[topicInfo.topic])
139761
+ topicPerf[topicInfo.topic] = { total: 0, count: 0 };
139762
+ topicPerf[topicInfo.topic].total += session.summary.percentage;
139763
+ topicPerf[topicInfo.topic].count++;
139764
+ }
139734
139765
  }
139735
- });
139766
+ );
139736
139767
  });
139737
139768
  const formatPerf = (perfData) => {
139738
139769
  return Object.entries(perfData).map(([name3, data]) => ({
139739
139770
  name: name3,
139740
139771
  totalSessions: data.count,
139741
- averageScore: parseFloat((data.total / data.count).toFixed(2))
139772
+ averageScore: parseFloat(
139773
+ (data.total / data.count).toFixed(2)
139774
+ )
139742
139775
  })).sort((a4, b2) => b2.totalSessions - a4.totalSessions);
139743
139776
  };
139744
139777
  return {
@@ -139750,11 +139783,6 @@ var PracticeHistoryService = class {
139750
139783
  performanceByTopic: formatPerf(topicPerf)
139751
139784
  };
139752
139785
  }
139753
- static clearHistory() {
139754
- if (typeof window !== "undefined") {
139755
- localStorage.removeItem(LOCAL_STORAGE_KEY);
139756
- }
139757
- }
139758
139786
  };
139759
139787
  // NEW: A static property to hold the injected sync provider
139760
139788
  PracticeHistoryService.syncProvider = null;
@@ -167238,15 +167266,19 @@ var ClientTranslation = ({ tKey, options, fallback: fallback2 }) => {
167238
167266
  };
167239
167267
 
167240
167268
  // src/react-ui/components/app/PersonalPracticeDashboard.tsx
167241
- var PersonalPracticeDashboard = ({ settingsPath }) => {
167269
+ var PersonalPracticeDashboard = ({ settingsPath, initialHistory, initialStats, isControlled = false }) => {
167242
167270
  const router = navigation.useRouter();
167243
167271
  const { toast: toast2 } = useToast();
167244
167272
  const { t: t4, i18n } = useTranslation();
167245
167273
  const [isMounted, setIsMounted] = React163.useState(false);
167246
167274
  React163.useEffect(() => setIsMounted(true), []);
167247
167275
  const [isLoading, setIsLoading] = React163.useState(true);
167248
- const [stats, setStats] = React163.useState(null);
167249
- const [history2, setHistory] = React163.useState([]);
167276
+ const [stats, setStats] = React163.useState(
167277
+ initialStats || null
167278
+ );
167279
+ const [history2, setHistory] = React163.useState(
167280
+ initialHistory || []
167281
+ );
167250
167282
  const [userName, setUserName] = React163.useState(null);
167251
167283
  const [allAchievements, setAllAchievements] = React163.useState([]);
167252
167284
  const [motivationalQuote, setMotivationalQuote] = React163.useState(null);
@@ -167260,6 +167292,10 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
167260
167292
  );
167261
167293
  const [isSettingsModalOpen, setIsSettingsModalOpen] = React163.useState(false);
167262
167294
  const loadDashboardData = React163.useCallback(() => {
167295
+ if (isControlled) {
167296
+ setIsLoading(false);
167297
+ return;
167298
+ }
167263
167299
  setIsLoading(true);
167264
167300
  setDashboardLayout(DashboardLayoutService.getLayout());
167265
167301
  const practiceHistory = PracticeHistoryService.getPracticeHistory();
@@ -167288,10 +167324,20 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
167288
167324
  if (KnowledgeCardService.getPendingConcepts().length > 0) {
167289
167325
  CardGenerationProcessor.processQueue();
167290
167326
  }
167291
- }, [toast2, t4]);
167327
+ }, [isControlled, toast2, t4]);
167292
167328
  React163.useEffect(() => {
167293
- loadDashboardData();
167294
- }, [loadDashboardData]);
167329
+ if (isControlled) {
167330
+ setHistory(initialHistory || []);
167331
+ setStats(initialStats || null);
167332
+ const name3 = UserConfigService.getFullName();
167333
+ setUserName(name3);
167334
+ const achievementsWithStatus = AchievementService.getAllAchievementsWithStatus();
167335
+ setAllAchievements(achievementsWithStatus);
167336
+ setIsLoading(false);
167337
+ } else {
167338
+ loadDashboardData();
167339
+ }
167340
+ }, [isControlled, initialHistory, initialStats, loadDashboardData]);
167295
167341
  React163.useEffect(() => {
167296
167342
  setMotivationalQuote(QuoteService.getRandomQuote());
167297
167343
  const fetchAIQuote = async () => {
@@ -1,8 +1,8 @@
1
1
  import { s as QuizConfig, g as QuizQuestion } from './quiz-config-o4j2dfsu.cjs';
2
2
  export { B as BaseQuestion, e as BlocklyProgrammingQuestion, C as CodingQuestion, D as DragAndDropQuestion, l as DraggableItem, m as DropZone, F as FillInTheBlanksQuestion, n as HotspotArea, H as HotspotQuestion, M as MarkdownString, k as MatchOptionItem, j as MatchPromptItem, d as MatchingQuestion, a as MultipleChoiceQuestion, b as MultipleResponseQuestion, N as NumericQuestion, h as QuestionOption, Q as QuestionTypeStrings, r as QuizSettings, R as RichContentString, q as SCORMSettings, f as ScratchProgrammingQuestion, i as SequenceItem, c as SequenceQuestion, S as ShortAnswerQuestion, p as SupportedCodingLanguage, o as TestCase, T as TrueFalseQuestion } from './quiz-config-o4j2dfsu.cjs';
3
3
  export { APIKeyService, AchievementService, Approach, ApproachTableRawDifficulty, BloomLevelName, BloomLevelType, Category, CodeNamedEntity, Context, GEMINI_API_KEY_SERVICE_NAME, GradeLevel, KnowledgeCardService, KnowledgeDimension, LearningObjective, LearningObjectiveMetadata, MetadataService, PracticeHistoryService, QuestionBankService, QuestionImportService, QuestionInBank, QuestionTypeType, QuizEditorService, QuizEngine, QuizEngineCallbacks, QuizEngineConstructorOptions, QuoteService, SCORMService, StandardDifficulty, Subject, Topic, UserConfigService, cn, emptyQuiz, exportQuizAsSCORMZip, generateLauncherHTML, generateSCORMManifest, generateUniqueId, sampleQuiz } from './index.cjs';
4
- import { Q as QuizResultType, U as UserAnswerType, m as PracticeSuggestion, l as PracticeSuggestionTopic, i as Achievement, o as PracticeStats, p as PracticeSessionSummary, h as QuizReviewContent } from './ai-ecosystem-DqFRlFU3.cjs';
5
- export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, n as PracticeSession, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqFRlFU3.cjs';
4
+ import { Q as QuizResultType, U as UserAnswerType, n as PracticeSession, o as PracticeStats, m as PracticeSuggestion, l as PracticeSuggestionTopic, i as Achievement, p as PracticeSessionSummary, h as QuizReviewContent } from './ai-ecosystem-DqFRlFU3.cjs';
5
+ export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqFRlFU3.cjs';
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode } from 'react';
8
8
  export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-DfLqsPwa.cjs';
@@ -72,6 +72,9 @@ declare const QuestionRenderer: React__default.ForwardRefExoticComponent<Questio
72
72
 
73
73
  interface PersonalPracticeDashboardProps {
74
74
  settingsPath?: string;
75
+ initialHistory?: PracticeSession[];
76
+ initialStats?: PracticeStats | null;
77
+ isControlled?: boolean;
75
78
  }
76
79
  declare const PersonalPracticeDashboard: React__default.FC<PersonalPracticeDashboardProps>;
77
80
 
@@ -215,4 +218,4 @@ declare const TabsList: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.Tab
215
218
  declare const TabsTrigger: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & React$1.RefAttributes<HTMLButtonElement>, "ref"> & React$1.RefAttributes<HTMLButtonElement>>;
216
219
  declare const TabsContent: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
217
220
 
218
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Achievement, Achievements, ActivityCalendar, Alert, AlertDescription, AlertTitle, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Cheatsheet, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, FreestyleQuizzesCard, GeneratedQuizzesCard, Input, Label, LanguageProvider, ManageTopics, PerformanceCharts, PersonalPracticeDashboard, PracticeHistoryTable, PracticeModeController, PracticeSessionSummary, PracticeStats, PracticeSuggestion, PracticeSuggestionTopic, Progress, QuestionRenderer, QuizConfig, QuizDataManagement, QuizPlayer, QuizQuestion, QuizResult, QuizResultType, QuizReview, QuizReviewContent, RadioGroup, RadioGroupItem, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SettingsModal, Skeleton, SuggestionDialog, Tabs, TabsContent, TabsList, TabsTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadResourceModal, UserAnswerType };
221
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Achievement, Achievements, ActivityCalendar, Alert, AlertDescription, AlertTitle, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Cheatsheet, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, FreestyleQuizzesCard, GeneratedQuizzesCard, Input, Label, LanguageProvider, ManageTopics, PerformanceCharts, PersonalPracticeDashboard, PracticeHistoryTable, PracticeModeController, PracticeSession, PracticeSessionSummary, PracticeStats, PracticeSuggestion, PracticeSuggestionTopic, Progress, QuestionRenderer, QuizConfig, QuizDataManagement, QuizPlayer, QuizQuestion, QuizResult, QuizResultType, QuizReview, QuizReviewContent, RadioGroup, RadioGroupItem, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SettingsModal, Skeleton, SuggestionDialog, Tabs, TabsContent, TabsList, TabsTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadResourceModal, UserAnswerType };
@@ -1,8 +1,8 @@
1
1
  import { s as QuizConfig, g as QuizQuestion } from './quiz-config-o4j2dfsu.js';
2
2
  export { B as BaseQuestion, e as BlocklyProgrammingQuestion, C as CodingQuestion, D as DragAndDropQuestion, l as DraggableItem, m as DropZone, F as FillInTheBlanksQuestion, n as HotspotArea, H as HotspotQuestion, M as MarkdownString, k as MatchOptionItem, j as MatchPromptItem, d as MatchingQuestion, a as MultipleChoiceQuestion, b as MultipleResponseQuestion, N as NumericQuestion, h as QuestionOption, Q as QuestionTypeStrings, r as QuizSettings, R as RichContentString, q as SCORMSettings, f as ScratchProgrammingQuestion, i as SequenceItem, c as SequenceQuestion, S as ShortAnswerQuestion, p as SupportedCodingLanguage, o as TestCase, T as TrueFalseQuestion } from './quiz-config-o4j2dfsu.js';
3
3
  export { APIKeyService, AchievementService, Approach, ApproachTableRawDifficulty, BloomLevelName, BloomLevelType, Category, CodeNamedEntity, Context, GEMINI_API_KEY_SERVICE_NAME, GradeLevel, KnowledgeCardService, KnowledgeDimension, LearningObjective, LearningObjectiveMetadata, MetadataService, PracticeHistoryService, QuestionBankService, QuestionImportService, QuestionInBank, QuestionTypeType, QuizEditorService, QuizEngine, QuizEngineCallbacks, QuizEngineConstructorOptions, QuoteService, SCORMService, StandardDifficulty, Subject, Topic, UserConfigService, cn, emptyQuiz, exportQuizAsSCORMZip, generateLauncherHTML, generateSCORMManifest, generateUniqueId, sampleQuiz } from './index.js';
4
- import { Q as QuizResultType, U as UserAnswerType, m as PracticeSuggestion, l as PracticeSuggestionTopic, i as Achievement, o as PracticeStats, p as PracticeSessionSummary, h as QuizReviewContent } from './ai-ecosystem-DqVlSO3r.js';
5
- export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, n as PracticeSession, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqVlSO3r.js';
4
+ import { Q as QuizResultType, U as UserAnswerType, n as PracticeSession, o as PracticeStats, m as PracticeSuggestion, l as PracticeSuggestionTopic, i as Achievement, p as PracticeSessionSummary, h as QuizReviewContent } from './ai-ecosystem-DqVlSO3r.js';
5
+ export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqVlSO3r.js';
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode } from 'react';
8
8
  export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-Z0Qz6kch.js';
@@ -72,6 +72,9 @@ declare const QuestionRenderer: React__default.ForwardRefExoticComponent<Questio
72
72
 
73
73
  interface PersonalPracticeDashboardProps {
74
74
  settingsPath?: string;
75
+ initialHistory?: PracticeSession[];
76
+ initialStats?: PracticeStats | null;
77
+ isControlled?: boolean;
75
78
  }
76
79
  declare const PersonalPracticeDashboard: React__default.FC<PersonalPracticeDashboardProps>;
77
80
 
@@ -215,4 +218,4 @@ declare const TabsList: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.Tab
215
218
  declare const TabsTrigger: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsTriggerProps & React$1.RefAttributes<HTMLButtonElement>, "ref"> & React$1.RefAttributes<HTMLButtonElement>>;
216
219
  declare const TabsContent: React$1.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsContentProps & React$1.RefAttributes<HTMLDivElement>, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
217
220
 
218
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Achievement, Achievements, ActivityCalendar, Alert, AlertDescription, AlertTitle, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Cheatsheet, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, FreestyleQuizzesCard, GeneratedQuizzesCard, Input, Label, LanguageProvider, ManageTopics, PerformanceCharts, PersonalPracticeDashboard, PracticeHistoryTable, PracticeModeController, PracticeSessionSummary, PracticeStats, PracticeSuggestion, PracticeSuggestionTopic, Progress, QuestionRenderer, QuizConfig, QuizDataManagement, QuizPlayer, QuizQuestion, QuizResult, QuizResultType, QuizReview, QuizReviewContent, RadioGroup, RadioGroupItem, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SettingsModal, Skeleton, SuggestionDialog, Tabs, TabsContent, TabsList, TabsTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadResourceModal, UserAnswerType };
221
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Achievement, Achievements, ActivityCalendar, Alert, AlertDescription, AlertTitle, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Cheatsheet, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, FreestyleQuizzesCard, GeneratedQuizzesCard, Input, Label, LanguageProvider, ManageTopics, PerformanceCharts, PersonalPracticeDashboard, PracticeHistoryTable, PracticeModeController, PracticeSession, PracticeSessionSummary, PracticeStats, PracticeSuggestion, PracticeSuggestionTopic, Progress, QuestionRenderer, QuizConfig, QuizDataManagement, QuizPlayer, QuizQuestion, QuizResult, QuizResultType, QuizReview, QuizReviewContent, RadioGroup, RadioGroupItem, ScrollArea, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, SettingsModal, Skeleton, SuggestionDialog, Tabs, TabsContent, TabsList, TabsTrigger, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadResourceModal, UserAnswerType };
package/dist/react-ui.mjs CHANGED
@@ -139561,20 +139561,30 @@ var PracticeHistoryService = class {
139561
139561
  static saveHistory(history2) {
139562
139562
  if (typeof window !== "undefined") {
139563
139563
  try {
139564
- localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(history2));
139564
+ localStorage.setItem(
139565
+ LOCAL_STORAGE_KEY,
139566
+ JSON.stringify(history2)
139567
+ );
139565
139568
  } catch (e3) {
139566
- console.error("Error saving practice history to localStorage:", e3);
139569
+ console.error(
139570
+ "Error saving practice history to localStorage:",
139571
+ e3
139572
+ );
139567
139573
  }
139568
139574
  }
139569
139575
  }
139570
139576
  static saveCompletedPracticeSession(quizConfig, result, review = null) {
139571
139577
  const history2 = this.getPracticeHistory();
139572
139578
  const topicsCovered = Array.from(
139573
- new Set(quizConfig.questions.map((q2) => JSON.stringify({
139574
- subject: q2.subject || "Uncategorized",
139575
- category: q2.category || "General",
139576
- topic: q2.topic || "General Topic"
139577
- })))
139579
+ new Set(
139580
+ quizConfig.questions.map(
139581
+ (q2) => JSON.stringify({
139582
+ subject: q2.subject || "Uncategorized",
139583
+ category: q2.category || "General",
139584
+ topic: q2.topic || "General Topic"
139585
+ })
139586
+ )
139587
+ )
139578
139588
  ).map((s4) => JSON.parse(s4));
139579
139589
  const newSession = {
139580
139590
  id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
@@ -139592,7 +139602,9 @@ var PracticeHistoryService = class {
139592
139602
  history2.unshift(newSession);
139593
139603
  this.saveHistory(history2);
139594
139604
  if (this.syncProvider) {
139595
- console.log(`Sync provider found. Pushing session ${newSession.id} to backend.`);
139605
+ console.log(
139606
+ `Sync provider found. Pushing session ${newSession.id} to backend.`
139607
+ );
139596
139608
  this.syncProvider.pushSession(newSession).catch((err) => {
139597
139609
  console.error("Sync provider failed to push session:", err);
139598
139610
  });
@@ -139606,7 +139618,9 @@ var PracticeHistoryService = class {
139606
139618
  history2[sessionIndex].quizReview = review;
139607
139619
  this.saveHistory(history2);
139608
139620
  } else {
139609
- console.warn(`Could not find session with ID "${sessionId}" to update review.`);
139621
+ console.warn(
139622
+ `Could not find session with ID "${sessionId}" to update review.`
139623
+ );
139610
139624
  }
139611
139625
  }
139612
139626
  static getPracticeSessionById(sessionId) {
@@ -139626,6 +139640,14 @@ var PracticeHistoryService = class {
139626
139640
  }
139627
139641
  static getPracticeStats() {
139628
139642
  const history2 = this.getPracticeHistory();
139643
+ return this.calculateStatsFromHistory(history2);
139644
+ }
139645
+ static clearHistory() {
139646
+ if (typeof window !== "undefined") {
139647
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
139648
+ }
139649
+ }
139650
+ static calculateStatsFromHistory(history2) {
139629
139651
  if (history2.length === 0) {
139630
139652
  return {
139631
139653
  totalSessions: 0,
@@ -139664,7 +139686,9 @@ var PracticeHistoryService = class {
139664
139686
  const currentDay = new Date(sortedDays[i2]);
139665
139687
  const nextDay = new Date(sortedDays[i2 + 1]);
139666
139688
  const diffTime = currentDay.getTime() - nextDay.getTime();
139667
- const diffDays = Math.round(diffTime / (1e3 * 60 * 60 * 24));
139689
+ const diffDays = Math.round(
139690
+ diffTime / (1e3 * 60 * 60 * 24)
139691
+ );
139668
139692
  if (diffDays === 1) {
139669
139693
  currentStreak++;
139670
139694
  } else {
@@ -139696,22 +139720,31 @@ var PracticeHistoryService = class {
139696
139720
  const subjectPerf = {};
139697
139721
  const topicPerf = {};
139698
139722
  history2.forEach((session) => {
139699
- session.summary.topics.forEach((topicInfo) => {
139700
- if (session.summary.percentage !== null) {
139701
- if (!subjectPerf[topicInfo.subject]) subjectPerf[topicInfo.subject] = { total: 0, count: 0 };
139702
- subjectPerf[topicInfo.subject].total += session.summary.percentage;
139703
- subjectPerf[topicInfo.subject].count++;
139704
- if (!topicPerf[topicInfo.topic]) topicPerf[topicInfo.topic] = { total: 0, count: 0 };
139705
- topicPerf[topicInfo.topic].total += session.summary.percentage;
139706
- topicPerf[topicInfo.topic].count++;
139723
+ session.summary.topics.forEach(
139724
+ (topicInfo) => {
139725
+ if (session.summary.percentage !== null) {
139726
+ if (!subjectPerf[topicInfo.subject])
139727
+ subjectPerf[topicInfo.subject] = {
139728
+ total: 0,
139729
+ count: 0
139730
+ };
139731
+ subjectPerf[topicInfo.subject].total += session.summary.percentage;
139732
+ subjectPerf[topicInfo.subject].count++;
139733
+ if (!topicPerf[topicInfo.topic])
139734
+ topicPerf[topicInfo.topic] = { total: 0, count: 0 };
139735
+ topicPerf[topicInfo.topic].total += session.summary.percentage;
139736
+ topicPerf[topicInfo.topic].count++;
139737
+ }
139707
139738
  }
139708
- });
139739
+ );
139709
139740
  });
139710
139741
  const formatPerf = (perfData) => {
139711
139742
  return Object.entries(perfData).map(([name3, data]) => ({
139712
139743
  name: name3,
139713
139744
  totalSessions: data.count,
139714
- averageScore: parseFloat((data.total / data.count).toFixed(2))
139745
+ averageScore: parseFloat(
139746
+ (data.total / data.count).toFixed(2)
139747
+ )
139715
139748
  })).sort((a4, b2) => b2.totalSessions - a4.totalSessions);
139716
139749
  };
139717
139750
  return {
@@ -139723,11 +139756,6 @@ var PracticeHistoryService = class {
139723
139756
  performanceByTopic: formatPerf(topicPerf)
139724
139757
  };
139725
139758
  }
139726
- static clearHistory() {
139727
- if (typeof window !== "undefined") {
139728
- localStorage.removeItem(LOCAL_STORAGE_KEY);
139729
- }
139730
- }
139731
139759
  };
139732
139760
  // NEW: A static property to hold the injected sync provider
139733
139761
  PracticeHistoryService.syncProvider = null;
@@ -167211,15 +167239,19 @@ var ClientTranslation = ({ tKey, options, fallback: fallback2 }) => {
167211
167239
  };
167212
167240
 
167213
167241
  // src/react-ui/components/app/PersonalPracticeDashboard.tsx
167214
- var PersonalPracticeDashboard = ({ settingsPath }) => {
167242
+ var PersonalPracticeDashboard = ({ settingsPath, initialHistory, initialStats, isControlled = false }) => {
167215
167243
  const router = useRouter();
167216
167244
  const { toast: toast2 } = useToast();
167217
167245
  const { t: t4, i18n } = useTranslation();
167218
167246
  const [isMounted, setIsMounted] = useState(false);
167219
167247
  useEffect(() => setIsMounted(true), []);
167220
167248
  const [isLoading, setIsLoading] = useState(true);
167221
- const [stats, setStats] = useState(null);
167222
- const [history2, setHistory] = useState([]);
167249
+ const [stats, setStats] = useState(
167250
+ initialStats || null
167251
+ );
167252
+ const [history2, setHistory] = useState(
167253
+ initialHistory || []
167254
+ );
167223
167255
  const [userName, setUserName] = useState(null);
167224
167256
  const [allAchievements, setAllAchievements] = useState([]);
167225
167257
  const [motivationalQuote, setMotivationalQuote] = useState(null);
@@ -167233,6 +167265,10 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
167233
167265
  );
167234
167266
  const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
167235
167267
  const loadDashboardData = useCallback(() => {
167268
+ if (isControlled) {
167269
+ setIsLoading(false);
167270
+ return;
167271
+ }
167236
167272
  setIsLoading(true);
167237
167273
  setDashboardLayout(DashboardLayoutService.getLayout());
167238
167274
  const practiceHistory = PracticeHistoryService.getPracticeHistory();
@@ -167261,10 +167297,20 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
167261
167297
  if (KnowledgeCardService.getPendingConcepts().length > 0) {
167262
167298
  CardGenerationProcessor.processQueue();
167263
167299
  }
167264
- }, [toast2, t4]);
167300
+ }, [isControlled, toast2, t4]);
167265
167301
  useEffect(() => {
167266
- loadDashboardData();
167267
- }, [loadDashboardData]);
167302
+ if (isControlled) {
167303
+ setHistory(initialHistory || []);
167304
+ setStats(initialStats || null);
167305
+ const name3 = UserConfigService.getFullName();
167306
+ setUserName(name3);
167307
+ const achievementsWithStatus = AchievementService.getAllAchievementsWithStatus();
167308
+ setAllAchievements(achievementsWithStatus);
167309
+ setIsLoading(false);
167310
+ } else {
167311
+ loadDashboardData();
167312
+ }
167313
+ }, [isControlled, initialHistory, initialStats, loadDashboardData]);
167268
167314
  useEffect(() => {
167269
167315
  setMotivationalQuote(QuoteService.getRandomQuote());
167270
167316
  const fetchAIQuote = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thanh01.pmt/interactive-quiz-kit",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "A comprehensive library for creating, managing, and playing interactive quizzes, with AI generation and SCORM support.",
5
5
  "keywords": [
6
6
  "react",