@thanh01.pmt/interactive-quiz-kit 1.0.46 → 1.0.48
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.
- package/dist/authoring.cjs +57 -19
- package/dist/authoring.mjs +57 -19
- package/dist/index.cjs +57 -19
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.mjs +57 -19
- package/dist/react-ui.cjs +81 -25
- package/dist/react-ui.d.cts +6 -3
- package/dist/react-ui.d.ts +6 -3
- package/dist/react-ui.mjs +81 -25
- package/package.json +1 -1
package/dist/authoring.cjs
CHANGED
|
@@ -3980,6 +3980,11 @@ var UserConfigService = class {
|
|
|
3980
3980
|
init_react_shim();
|
|
3981
3981
|
var LOCAL_STORAGE_KEY = "iqk_practice_history_v2";
|
|
3982
3982
|
var PracticeHistoryService = class {
|
|
3983
|
+
// NEW: A configuration method to allow the host app (QMS) to inject its sync logic
|
|
3984
|
+
static configure(provider) {
|
|
3985
|
+
this.syncProvider = provider.syncProvider;
|
|
3986
|
+
console.log("PracticeHistoryService configured with a sync provider.");
|
|
3987
|
+
}
|
|
3983
3988
|
static getPracticeHistory() {
|
|
3984
3989
|
if (typeof window === "undefined") return [];
|
|
3985
3990
|
try {
|
|
@@ -3993,20 +3998,30 @@ var PracticeHistoryService = class {
|
|
|
3993
3998
|
static saveHistory(history2) {
|
|
3994
3999
|
if (typeof window !== "undefined") {
|
|
3995
4000
|
try {
|
|
3996
|
-
localStorage.setItem(
|
|
4001
|
+
localStorage.setItem(
|
|
4002
|
+
LOCAL_STORAGE_KEY,
|
|
4003
|
+
JSON.stringify(history2)
|
|
4004
|
+
);
|
|
3997
4005
|
} catch (e2) {
|
|
3998
|
-
console.error(
|
|
4006
|
+
console.error(
|
|
4007
|
+
"Error saving practice history to localStorage:",
|
|
4008
|
+
e2
|
|
4009
|
+
);
|
|
3999
4010
|
}
|
|
4000
4011
|
}
|
|
4001
4012
|
}
|
|
4002
4013
|
static saveCompletedPracticeSession(quizConfig, result, review = null) {
|
|
4003
4014
|
const history2 = this.getPracticeHistory();
|
|
4004
4015
|
const topicsCovered = Array.from(
|
|
4005
|
-
new Set(
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
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
|
+
)
|
|
4010
4025
|
).map((s2) => JSON.parse(s2));
|
|
4011
4026
|
const newSession = {
|
|
4012
4027
|
id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
@@ -4023,6 +4038,14 @@ var PracticeHistoryService = class {
|
|
|
4023
4038
|
};
|
|
4024
4039
|
history2.unshift(newSession);
|
|
4025
4040
|
this.saveHistory(history2);
|
|
4041
|
+
if (this.syncProvider) {
|
|
4042
|
+
console.log(
|
|
4043
|
+
`Sync provider found. Pushing session ${newSession.id} to backend.`
|
|
4044
|
+
);
|
|
4045
|
+
this.syncProvider.pushSession(newSession).catch((err) => {
|
|
4046
|
+
console.error("Sync provider failed to push session:", err);
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4026
4049
|
return newSession.id;
|
|
4027
4050
|
}
|
|
4028
4051
|
static updatePracticeReview(sessionId, review) {
|
|
@@ -4032,7 +4055,9 @@ var PracticeHistoryService = class {
|
|
|
4032
4055
|
history2[sessionIndex].quizReview = review;
|
|
4033
4056
|
this.saveHistory(history2);
|
|
4034
4057
|
} else {
|
|
4035
|
-
console.warn(
|
|
4058
|
+
console.warn(
|
|
4059
|
+
`Could not find session with ID "${sessionId}" to update review.`
|
|
4060
|
+
);
|
|
4036
4061
|
}
|
|
4037
4062
|
}
|
|
4038
4063
|
static getPracticeSessionById(sessionId) {
|
|
@@ -4090,7 +4115,9 @@ var PracticeHistoryService = class {
|
|
|
4090
4115
|
const currentDay = new Date(sortedDays[i]);
|
|
4091
4116
|
const nextDay = new Date(sortedDays[i + 1]);
|
|
4092
4117
|
const diffTime = currentDay.getTime() - nextDay.getTime();
|
|
4093
|
-
const diffDays = Math.round(
|
|
4118
|
+
const diffDays = Math.round(
|
|
4119
|
+
diffTime / (1e3 * 60 * 60 * 24)
|
|
4120
|
+
);
|
|
4094
4121
|
if (diffDays === 1) {
|
|
4095
4122
|
currentStreak++;
|
|
4096
4123
|
} else {
|
|
@@ -4122,22 +4149,31 @@ var PracticeHistoryService = class {
|
|
|
4122
4149
|
const subjectPerf = {};
|
|
4123
4150
|
const topicPerf = {};
|
|
4124
4151
|
history2.forEach((session) => {
|
|
4125
|
-
session.summary.topics.forEach(
|
|
4126
|
-
|
|
4127
|
-
if (
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4152
|
+
session.summary.topics.forEach(
|
|
4153
|
+
(topicInfo) => {
|
|
4154
|
+
if (session.summary.percentage !== null) {
|
|
4155
|
+
if (!subjectPerf[topicInfo.subject])
|
|
4156
|
+
subjectPerf[topicInfo.subject] = {
|
|
4157
|
+
total: 0,
|
|
4158
|
+
count: 0
|
|
4159
|
+
};
|
|
4160
|
+
subjectPerf[topicInfo.subject].total += session.summary.percentage;
|
|
4161
|
+
subjectPerf[topicInfo.subject].count++;
|
|
4162
|
+
if (!topicPerf[topicInfo.topic])
|
|
4163
|
+
topicPerf[topicInfo.topic] = { total: 0, count: 0 };
|
|
4164
|
+
topicPerf[topicInfo.topic].total += session.summary.percentage;
|
|
4165
|
+
topicPerf[topicInfo.topic].count++;
|
|
4166
|
+
}
|
|
4133
4167
|
}
|
|
4134
|
-
|
|
4168
|
+
);
|
|
4135
4169
|
});
|
|
4136
4170
|
const formatPerf = (perfData) => {
|
|
4137
4171
|
return Object.entries(perfData).map(([name3, data]) => ({
|
|
4138
4172
|
name: name3,
|
|
4139
4173
|
totalSessions: data.count,
|
|
4140
|
-
averageScore: parseFloat(
|
|
4174
|
+
averageScore: parseFloat(
|
|
4175
|
+
(data.total / data.count).toFixed(2)
|
|
4176
|
+
)
|
|
4141
4177
|
})).sort((a2, b) => b.totalSessions - a2.totalSessions);
|
|
4142
4178
|
};
|
|
4143
4179
|
return {
|
|
@@ -4155,6 +4191,8 @@ var PracticeHistoryService = class {
|
|
|
4155
4191
|
}
|
|
4156
4192
|
}
|
|
4157
4193
|
};
|
|
4194
|
+
// NEW: A static property to hold the injected sync provider
|
|
4195
|
+
PracticeHistoryService.syncProvider = null;
|
|
4158
4196
|
|
|
4159
4197
|
// src/services/AchievementService.ts
|
|
4160
4198
|
init_react_shim();
|
package/dist/authoring.mjs
CHANGED
|
@@ -3954,6 +3954,11 @@ var UserConfigService = class {
|
|
|
3954
3954
|
init_react_shim();
|
|
3955
3955
|
var LOCAL_STORAGE_KEY = "iqk_practice_history_v2";
|
|
3956
3956
|
var PracticeHistoryService = class {
|
|
3957
|
+
// NEW: A configuration method to allow the host app (QMS) to inject its sync logic
|
|
3958
|
+
static configure(provider) {
|
|
3959
|
+
this.syncProvider = provider.syncProvider;
|
|
3960
|
+
console.log("PracticeHistoryService configured with a sync provider.");
|
|
3961
|
+
}
|
|
3957
3962
|
static getPracticeHistory() {
|
|
3958
3963
|
if (typeof window === "undefined") return [];
|
|
3959
3964
|
try {
|
|
@@ -3967,20 +3972,30 @@ var PracticeHistoryService = class {
|
|
|
3967
3972
|
static saveHistory(history2) {
|
|
3968
3973
|
if (typeof window !== "undefined") {
|
|
3969
3974
|
try {
|
|
3970
|
-
localStorage.setItem(
|
|
3975
|
+
localStorage.setItem(
|
|
3976
|
+
LOCAL_STORAGE_KEY,
|
|
3977
|
+
JSON.stringify(history2)
|
|
3978
|
+
);
|
|
3971
3979
|
} catch (e2) {
|
|
3972
|
-
console.error(
|
|
3980
|
+
console.error(
|
|
3981
|
+
"Error saving practice history to localStorage:",
|
|
3982
|
+
e2
|
|
3983
|
+
);
|
|
3973
3984
|
}
|
|
3974
3985
|
}
|
|
3975
3986
|
}
|
|
3976
3987
|
static saveCompletedPracticeSession(quizConfig, result, review = null) {
|
|
3977
3988
|
const history2 = this.getPracticeHistory();
|
|
3978
3989
|
const topicsCovered = Array.from(
|
|
3979
|
-
new Set(
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
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
|
+
)
|
|
3984
3999
|
).map((s2) => JSON.parse(s2));
|
|
3985
4000
|
const newSession = {
|
|
3986
4001
|
id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
@@ -3997,6 +4012,14 @@ var PracticeHistoryService = class {
|
|
|
3997
4012
|
};
|
|
3998
4013
|
history2.unshift(newSession);
|
|
3999
4014
|
this.saveHistory(history2);
|
|
4015
|
+
if (this.syncProvider) {
|
|
4016
|
+
console.log(
|
|
4017
|
+
`Sync provider found. Pushing session ${newSession.id} to backend.`
|
|
4018
|
+
);
|
|
4019
|
+
this.syncProvider.pushSession(newSession).catch((err) => {
|
|
4020
|
+
console.error("Sync provider failed to push session:", err);
|
|
4021
|
+
});
|
|
4022
|
+
}
|
|
4000
4023
|
return newSession.id;
|
|
4001
4024
|
}
|
|
4002
4025
|
static updatePracticeReview(sessionId, review) {
|
|
@@ -4006,7 +4029,9 @@ var PracticeHistoryService = class {
|
|
|
4006
4029
|
history2[sessionIndex].quizReview = review;
|
|
4007
4030
|
this.saveHistory(history2);
|
|
4008
4031
|
} else {
|
|
4009
|
-
console.warn(
|
|
4032
|
+
console.warn(
|
|
4033
|
+
`Could not find session with ID "${sessionId}" to update review.`
|
|
4034
|
+
);
|
|
4010
4035
|
}
|
|
4011
4036
|
}
|
|
4012
4037
|
static getPracticeSessionById(sessionId) {
|
|
@@ -4064,7 +4089,9 @@ var PracticeHistoryService = class {
|
|
|
4064
4089
|
const currentDay = new Date(sortedDays[i]);
|
|
4065
4090
|
const nextDay = new Date(sortedDays[i + 1]);
|
|
4066
4091
|
const diffTime = currentDay.getTime() - nextDay.getTime();
|
|
4067
|
-
const diffDays = Math.round(
|
|
4092
|
+
const diffDays = Math.round(
|
|
4093
|
+
diffTime / (1e3 * 60 * 60 * 24)
|
|
4094
|
+
);
|
|
4068
4095
|
if (diffDays === 1) {
|
|
4069
4096
|
currentStreak++;
|
|
4070
4097
|
} else {
|
|
@@ -4096,22 +4123,31 @@ var PracticeHistoryService = class {
|
|
|
4096
4123
|
const subjectPerf = {};
|
|
4097
4124
|
const topicPerf = {};
|
|
4098
4125
|
history2.forEach((session) => {
|
|
4099
|
-
session.summary.topics.forEach(
|
|
4100
|
-
|
|
4101
|
-
if (
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4126
|
+
session.summary.topics.forEach(
|
|
4127
|
+
(topicInfo) => {
|
|
4128
|
+
if (session.summary.percentage !== null) {
|
|
4129
|
+
if (!subjectPerf[topicInfo.subject])
|
|
4130
|
+
subjectPerf[topicInfo.subject] = {
|
|
4131
|
+
total: 0,
|
|
4132
|
+
count: 0
|
|
4133
|
+
};
|
|
4134
|
+
subjectPerf[topicInfo.subject].total += session.summary.percentage;
|
|
4135
|
+
subjectPerf[topicInfo.subject].count++;
|
|
4136
|
+
if (!topicPerf[topicInfo.topic])
|
|
4137
|
+
topicPerf[topicInfo.topic] = { total: 0, count: 0 };
|
|
4138
|
+
topicPerf[topicInfo.topic].total += session.summary.percentage;
|
|
4139
|
+
topicPerf[topicInfo.topic].count++;
|
|
4140
|
+
}
|
|
4107
4141
|
}
|
|
4108
|
-
|
|
4142
|
+
);
|
|
4109
4143
|
});
|
|
4110
4144
|
const formatPerf = (perfData) => {
|
|
4111
4145
|
return Object.entries(perfData).map(([name3, data]) => ({
|
|
4112
4146
|
name: name3,
|
|
4113
4147
|
totalSessions: data.count,
|
|
4114
|
-
averageScore: parseFloat(
|
|
4148
|
+
averageScore: parseFloat(
|
|
4149
|
+
(data.total / data.count).toFixed(2)
|
|
4150
|
+
)
|
|
4115
4151
|
})).sort((a2, b) => b.totalSessions - a2.totalSessions);
|
|
4116
4152
|
};
|
|
4117
4153
|
return {
|
|
@@ -4129,6 +4165,8 @@ var PracticeHistoryService = class {
|
|
|
4129
4165
|
}
|
|
4130
4166
|
}
|
|
4131
4167
|
};
|
|
4168
|
+
// NEW: A static property to hold the injected sync provider
|
|
4169
|
+
PracticeHistoryService.syncProvider = null;
|
|
4132
4170
|
|
|
4133
4171
|
// src/services/AchievementService.ts
|
|
4134
4172
|
init_react_shim();
|
package/dist/index.cjs
CHANGED
|
@@ -1946,6 +1946,11 @@ var UserConfigService = class {
|
|
|
1946
1946
|
// src/services/PracticeHistoryService.ts
|
|
1947
1947
|
var LOCAL_STORAGE_KEY = "iqk_practice_history_v2";
|
|
1948
1948
|
var PracticeHistoryService = class {
|
|
1949
|
+
// NEW: A configuration method to allow the host app (QMS) to inject its sync logic
|
|
1950
|
+
static configure(provider) {
|
|
1951
|
+
this.syncProvider = provider.syncProvider;
|
|
1952
|
+
console.log("PracticeHistoryService configured with a sync provider.");
|
|
1953
|
+
}
|
|
1949
1954
|
static getPracticeHistory() {
|
|
1950
1955
|
if (typeof window === "undefined") return [];
|
|
1951
1956
|
try {
|
|
@@ -1959,20 +1964,30 @@ var PracticeHistoryService = class {
|
|
|
1959
1964
|
static saveHistory(history) {
|
|
1960
1965
|
if (typeof window !== "undefined") {
|
|
1961
1966
|
try {
|
|
1962
|
-
localStorage.setItem(
|
|
1967
|
+
localStorage.setItem(
|
|
1968
|
+
LOCAL_STORAGE_KEY,
|
|
1969
|
+
JSON.stringify(history)
|
|
1970
|
+
);
|
|
1963
1971
|
} catch (e) {
|
|
1964
|
-
console.error(
|
|
1972
|
+
console.error(
|
|
1973
|
+
"Error saving practice history to localStorage:",
|
|
1974
|
+
e
|
|
1975
|
+
);
|
|
1965
1976
|
}
|
|
1966
1977
|
}
|
|
1967
1978
|
}
|
|
1968
1979
|
static saveCompletedPracticeSession(quizConfig, result, review = null) {
|
|
1969
1980
|
const history = this.getPracticeHistory();
|
|
1970
1981
|
const topicsCovered = Array.from(
|
|
1971
|
-
new Set(
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
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
|
+
)
|
|
1976
1991
|
).map((s) => JSON.parse(s));
|
|
1977
1992
|
const newSession = {
|
|
1978
1993
|
id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
@@ -1989,6 +2004,14 @@ var PracticeHistoryService = class {
|
|
|
1989
2004
|
};
|
|
1990
2005
|
history.unshift(newSession);
|
|
1991
2006
|
this.saveHistory(history);
|
|
2007
|
+
if (this.syncProvider) {
|
|
2008
|
+
console.log(
|
|
2009
|
+
`Sync provider found. Pushing session ${newSession.id} to backend.`
|
|
2010
|
+
);
|
|
2011
|
+
this.syncProvider.pushSession(newSession).catch((err) => {
|
|
2012
|
+
console.error("Sync provider failed to push session:", err);
|
|
2013
|
+
});
|
|
2014
|
+
}
|
|
1992
2015
|
return newSession.id;
|
|
1993
2016
|
}
|
|
1994
2017
|
static updatePracticeReview(sessionId, review) {
|
|
@@ -1998,7 +2021,9 @@ var PracticeHistoryService = class {
|
|
|
1998
2021
|
history[sessionIndex].quizReview = review;
|
|
1999
2022
|
this.saveHistory(history);
|
|
2000
2023
|
} else {
|
|
2001
|
-
console.warn(
|
|
2024
|
+
console.warn(
|
|
2025
|
+
`Could not find session with ID "${sessionId}" to update review.`
|
|
2026
|
+
);
|
|
2002
2027
|
}
|
|
2003
2028
|
}
|
|
2004
2029
|
static getPracticeSessionById(sessionId) {
|
|
@@ -2056,7 +2081,9 @@ var PracticeHistoryService = class {
|
|
|
2056
2081
|
const currentDay = new Date(sortedDays[i]);
|
|
2057
2082
|
const nextDay = new Date(sortedDays[i + 1]);
|
|
2058
2083
|
const diffTime = currentDay.getTime() - nextDay.getTime();
|
|
2059
|
-
const diffDays = Math.round(
|
|
2084
|
+
const diffDays = Math.round(
|
|
2085
|
+
diffTime / (1e3 * 60 * 60 * 24)
|
|
2086
|
+
);
|
|
2060
2087
|
if (diffDays === 1) {
|
|
2061
2088
|
currentStreak++;
|
|
2062
2089
|
} else {
|
|
@@ -2088,22 +2115,31 @@ var PracticeHistoryService = class {
|
|
|
2088
2115
|
const subjectPerf = {};
|
|
2089
2116
|
const topicPerf = {};
|
|
2090
2117
|
history.forEach((session) => {
|
|
2091
|
-
session.summary.topics.forEach(
|
|
2092
|
-
|
|
2093
|
-
if (
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2118
|
+
session.summary.topics.forEach(
|
|
2119
|
+
(topicInfo) => {
|
|
2120
|
+
if (session.summary.percentage !== null) {
|
|
2121
|
+
if (!subjectPerf[topicInfo.subject])
|
|
2122
|
+
subjectPerf[topicInfo.subject] = {
|
|
2123
|
+
total: 0,
|
|
2124
|
+
count: 0
|
|
2125
|
+
};
|
|
2126
|
+
subjectPerf[topicInfo.subject].total += session.summary.percentage;
|
|
2127
|
+
subjectPerf[topicInfo.subject].count++;
|
|
2128
|
+
if (!topicPerf[topicInfo.topic])
|
|
2129
|
+
topicPerf[topicInfo.topic] = { total: 0, count: 0 };
|
|
2130
|
+
topicPerf[topicInfo.topic].total += session.summary.percentage;
|
|
2131
|
+
topicPerf[topicInfo.topic].count++;
|
|
2132
|
+
}
|
|
2099
2133
|
}
|
|
2100
|
-
|
|
2134
|
+
);
|
|
2101
2135
|
});
|
|
2102
2136
|
const formatPerf = (perfData) => {
|
|
2103
2137
|
return Object.entries(perfData).map(([name, data]) => ({
|
|
2104
2138
|
name,
|
|
2105
2139
|
totalSessions: data.count,
|
|
2106
|
-
averageScore: parseFloat(
|
|
2140
|
+
averageScore: parseFloat(
|
|
2141
|
+
(data.total / data.count).toFixed(2)
|
|
2142
|
+
)
|
|
2107
2143
|
})).sort((a, b) => b.totalSessions - a.totalSessions);
|
|
2108
2144
|
};
|
|
2109
2145
|
return {
|
|
@@ -2121,6 +2157,8 @@ var PracticeHistoryService = class {
|
|
|
2121
2157
|
}
|
|
2122
2158
|
}
|
|
2123
2159
|
};
|
|
2160
|
+
// NEW: A static property to hold the injected sync provider
|
|
2161
|
+
PracticeHistoryService.syncProvider = null;
|
|
2124
2162
|
|
|
2125
2163
|
// src/data/achievements.json
|
|
2126
2164
|
var achievements_default = [
|
package/dist/index.d.cts
CHANGED
|
@@ -230,7 +230,14 @@ declare class UserConfigService {
|
|
|
230
230
|
static deleteGoal(goalId: string): void;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
interface SyncProvider {
|
|
234
|
+
pushSession: (session: PracticeSession) => Promise<void>;
|
|
235
|
+
}
|
|
233
236
|
declare class PracticeHistoryService {
|
|
237
|
+
private static syncProvider;
|
|
238
|
+
static configure(provider: {
|
|
239
|
+
syncProvider: SyncProvider;
|
|
240
|
+
}): void;
|
|
234
241
|
static getPracticeHistory(): PracticeSession[];
|
|
235
242
|
private static saveHistory;
|
|
236
243
|
static saveCompletedPracticeSession(quizConfig: QuizConfig, result: QuizResultType, review?: QuizReviewContent | null): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -230,7 +230,14 @@ declare class UserConfigService {
|
|
|
230
230
|
static deleteGoal(goalId: string): void;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
interface SyncProvider {
|
|
234
|
+
pushSession: (session: PracticeSession) => Promise<void>;
|
|
235
|
+
}
|
|
233
236
|
declare class PracticeHistoryService {
|
|
237
|
+
private static syncProvider;
|
|
238
|
+
static configure(provider: {
|
|
239
|
+
syncProvider: SyncProvider;
|
|
240
|
+
}): void;
|
|
234
241
|
static getPracticeHistory(): PracticeSession[];
|
|
235
242
|
private static saveHistory;
|
|
236
243
|
static saveCompletedPracticeSession(quizConfig: QuizConfig, result: QuizResultType, review?: QuizReviewContent | null): string;
|
package/dist/index.mjs
CHANGED
|
@@ -1940,6 +1940,11 @@ var UserConfigService = class {
|
|
|
1940
1940
|
// src/services/PracticeHistoryService.ts
|
|
1941
1941
|
var LOCAL_STORAGE_KEY = "iqk_practice_history_v2";
|
|
1942
1942
|
var PracticeHistoryService = class {
|
|
1943
|
+
// NEW: A configuration method to allow the host app (QMS) to inject its sync logic
|
|
1944
|
+
static configure(provider) {
|
|
1945
|
+
this.syncProvider = provider.syncProvider;
|
|
1946
|
+
console.log("PracticeHistoryService configured with a sync provider.");
|
|
1947
|
+
}
|
|
1943
1948
|
static getPracticeHistory() {
|
|
1944
1949
|
if (typeof window === "undefined") return [];
|
|
1945
1950
|
try {
|
|
@@ -1953,20 +1958,30 @@ var PracticeHistoryService = class {
|
|
|
1953
1958
|
static saveHistory(history) {
|
|
1954
1959
|
if (typeof window !== "undefined") {
|
|
1955
1960
|
try {
|
|
1956
|
-
localStorage.setItem(
|
|
1961
|
+
localStorage.setItem(
|
|
1962
|
+
LOCAL_STORAGE_KEY,
|
|
1963
|
+
JSON.stringify(history)
|
|
1964
|
+
);
|
|
1957
1965
|
} catch (e) {
|
|
1958
|
-
console.error(
|
|
1966
|
+
console.error(
|
|
1967
|
+
"Error saving practice history to localStorage:",
|
|
1968
|
+
e
|
|
1969
|
+
);
|
|
1959
1970
|
}
|
|
1960
1971
|
}
|
|
1961
1972
|
}
|
|
1962
1973
|
static saveCompletedPracticeSession(quizConfig, result, review = null) {
|
|
1963
1974
|
const history = this.getPracticeHistory();
|
|
1964
1975
|
const topicsCovered = Array.from(
|
|
1965
|
-
new Set(
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
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
|
+
)
|
|
1970
1985
|
).map((s) => JSON.parse(s));
|
|
1971
1986
|
const newSession = {
|
|
1972
1987
|
id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
@@ -1983,6 +1998,14 @@ var PracticeHistoryService = class {
|
|
|
1983
1998
|
};
|
|
1984
1999
|
history.unshift(newSession);
|
|
1985
2000
|
this.saveHistory(history);
|
|
2001
|
+
if (this.syncProvider) {
|
|
2002
|
+
console.log(
|
|
2003
|
+
`Sync provider found. Pushing session ${newSession.id} to backend.`
|
|
2004
|
+
);
|
|
2005
|
+
this.syncProvider.pushSession(newSession).catch((err) => {
|
|
2006
|
+
console.error("Sync provider failed to push session:", err);
|
|
2007
|
+
});
|
|
2008
|
+
}
|
|
1986
2009
|
return newSession.id;
|
|
1987
2010
|
}
|
|
1988
2011
|
static updatePracticeReview(sessionId, review) {
|
|
@@ -1992,7 +2015,9 @@ var PracticeHistoryService = class {
|
|
|
1992
2015
|
history[sessionIndex].quizReview = review;
|
|
1993
2016
|
this.saveHistory(history);
|
|
1994
2017
|
} else {
|
|
1995
|
-
console.warn(
|
|
2018
|
+
console.warn(
|
|
2019
|
+
`Could not find session with ID "${sessionId}" to update review.`
|
|
2020
|
+
);
|
|
1996
2021
|
}
|
|
1997
2022
|
}
|
|
1998
2023
|
static getPracticeSessionById(sessionId) {
|
|
@@ -2050,7 +2075,9 @@ var PracticeHistoryService = class {
|
|
|
2050
2075
|
const currentDay = new Date(sortedDays[i]);
|
|
2051
2076
|
const nextDay = new Date(sortedDays[i + 1]);
|
|
2052
2077
|
const diffTime = currentDay.getTime() - nextDay.getTime();
|
|
2053
|
-
const diffDays = Math.round(
|
|
2078
|
+
const diffDays = Math.round(
|
|
2079
|
+
diffTime / (1e3 * 60 * 60 * 24)
|
|
2080
|
+
);
|
|
2054
2081
|
if (diffDays === 1) {
|
|
2055
2082
|
currentStreak++;
|
|
2056
2083
|
} else {
|
|
@@ -2082,22 +2109,31 @@ var PracticeHistoryService = class {
|
|
|
2082
2109
|
const subjectPerf = {};
|
|
2083
2110
|
const topicPerf = {};
|
|
2084
2111
|
history.forEach((session) => {
|
|
2085
|
-
session.summary.topics.forEach(
|
|
2086
|
-
|
|
2087
|
-
if (
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2112
|
+
session.summary.topics.forEach(
|
|
2113
|
+
(topicInfo) => {
|
|
2114
|
+
if (session.summary.percentage !== null) {
|
|
2115
|
+
if (!subjectPerf[topicInfo.subject])
|
|
2116
|
+
subjectPerf[topicInfo.subject] = {
|
|
2117
|
+
total: 0,
|
|
2118
|
+
count: 0
|
|
2119
|
+
};
|
|
2120
|
+
subjectPerf[topicInfo.subject].total += session.summary.percentage;
|
|
2121
|
+
subjectPerf[topicInfo.subject].count++;
|
|
2122
|
+
if (!topicPerf[topicInfo.topic])
|
|
2123
|
+
topicPerf[topicInfo.topic] = { total: 0, count: 0 };
|
|
2124
|
+
topicPerf[topicInfo.topic].total += session.summary.percentage;
|
|
2125
|
+
topicPerf[topicInfo.topic].count++;
|
|
2126
|
+
}
|
|
2093
2127
|
}
|
|
2094
|
-
|
|
2128
|
+
);
|
|
2095
2129
|
});
|
|
2096
2130
|
const formatPerf = (perfData) => {
|
|
2097
2131
|
return Object.entries(perfData).map(([name, data]) => ({
|
|
2098
2132
|
name,
|
|
2099
2133
|
totalSessions: data.count,
|
|
2100
|
-
averageScore: parseFloat(
|
|
2134
|
+
averageScore: parseFloat(
|
|
2135
|
+
(data.total / data.count).toFixed(2)
|
|
2136
|
+
)
|
|
2101
2137
|
})).sort((a, b) => b.totalSessions - a.totalSessions);
|
|
2102
2138
|
};
|
|
2103
2139
|
return {
|
|
@@ -2115,6 +2151,8 @@ var PracticeHistoryService = class {
|
|
|
2115
2151
|
}
|
|
2116
2152
|
}
|
|
2117
2153
|
};
|
|
2154
|
+
// NEW: A static property to hold the injected sync provider
|
|
2155
|
+
PracticeHistoryService.syncProvider = null;
|
|
2118
2156
|
|
|
2119
2157
|
// src/data/achievements.json
|
|
2120
2158
|
var achievements_default = [
|
package/dist/react-ui.cjs
CHANGED
|
@@ -139570,6 +139570,11 @@ AlertDialogCancel2.displayName = Cancel.displayName;
|
|
|
139570
139570
|
init_react_shim();
|
|
139571
139571
|
var LOCAL_STORAGE_KEY = "iqk_practice_history_v2";
|
|
139572
139572
|
var PracticeHistoryService = class {
|
|
139573
|
+
// NEW: A configuration method to allow the host app (QMS) to inject its sync logic
|
|
139574
|
+
static configure(provider) {
|
|
139575
|
+
this.syncProvider = provider.syncProvider;
|
|
139576
|
+
console.log("PracticeHistoryService configured with a sync provider.");
|
|
139577
|
+
}
|
|
139573
139578
|
static getPracticeHistory() {
|
|
139574
139579
|
if (typeof window === "undefined") return [];
|
|
139575
139580
|
try {
|
|
@@ -139583,20 +139588,30 @@ var PracticeHistoryService = class {
|
|
|
139583
139588
|
static saveHistory(history2) {
|
|
139584
139589
|
if (typeof window !== "undefined") {
|
|
139585
139590
|
try {
|
|
139586
|
-
localStorage.setItem(
|
|
139591
|
+
localStorage.setItem(
|
|
139592
|
+
LOCAL_STORAGE_KEY,
|
|
139593
|
+
JSON.stringify(history2)
|
|
139594
|
+
);
|
|
139587
139595
|
} catch (e3) {
|
|
139588
|
-
console.error(
|
|
139596
|
+
console.error(
|
|
139597
|
+
"Error saving practice history to localStorage:",
|
|
139598
|
+
e3
|
|
139599
|
+
);
|
|
139589
139600
|
}
|
|
139590
139601
|
}
|
|
139591
139602
|
}
|
|
139592
139603
|
static saveCompletedPracticeSession(quizConfig, result, review = null) {
|
|
139593
139604
|
const history2 = this.getPracticeHistory();
|
|
139594
139605
|
const topicsCovered = Array.from(
|
|
139595
|
-
new Set(
|
|
139596
|
-
|
|
139597
|
-
|
|
139598
|
-
|
|
139599
|
-
|
|
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
|
+
)
|
|
139600
139615
|
).map((s4) => JSON.parse(s4));
|
|
139601
139616
|
const newSession = {
|
|
139602
139617
|
id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
@@ -139613,6 +139628,14 @@ var PracticeHistoryService = class {
|
|
|
139613
139628
|
};
|
|
139614
139629
|
history2.unshift(newSession);
|
|
139615
139630
|
this.saveHistory(history2);
|
|
139631
|
+
if (this.syncProvider) {
|
|
139632
|
+
console.log(
|
|
139633
|
+
`Sync provider found. Pushing session ${newSession.id} to backend.`
|
|
139634
|
+
);
|
|
139635
|
+
this.syncProvider.pushSession(newSession).catch((err) => {
|
|
139636
|
+
console.error("Sync provider failed to push session:", err);
|
|
139637
|
+
});
|
|
139638
|
+
}
|
|
139616
139639
|
return newSession.id;
|
|
139617
139640
|
}
|
|
139618
139641
|
static updatePracticeReview(sessionId, review) {
|
|
@@ -139622,7 +139645,9 @@ var PracticeHistoryService = class {
|
|
|
139622
139645
|
history2[sessionIndex].quizReview = review;
|
|
139623
139646
|
this.saveHistory(history2);
|
|
139624
139647
|
} else {
|
|
139625
|
-
console.warn(
|
|
139648
|
+
console.warn(
|
|
139649
|
+
`Could not find session with ID "${sessionId}" to update review.`
|
|
139650
|
+
);
|
|
139626
139651
|
}
|
|
139627
139652
|
}
|
|
139628
139653
|
static getPracticeSessionById(sessionId) {
|
|
@@ -139680,7 +139705,9 @@ var PracticeHistoryService = class {
|
|
|
139680
139705
|
const currentDay = new Date(sortedDays[i2]);
|
|
139681
139706
|
const nextDay = new Date(sortedDays[i2 + 1]);
|
|
139682
139707
|
const diffTime = currentDay.getTime() - nextDay.getTime();
|
|
139683
|
-
const diffDays = Math.round(
|
|
139708
|
+
const diffDays = Math.round(
|
|
139709
|
+
diffTime / (1e3 * 60 * 60 * 24)
|
|
139710
|
+
);
|
|
139684
139711
|
if (diffDays === 1) {
|
|
139685
139712
|
currentStreak++;
|
|
139686
139713
|
} else {
|
|
@@ -139712,22 +139739,31 @@ var PracticeHistoryService = class {
|
|
|
139712
139739
|
const subjectPerf = {};
|
|
139713
139740
|
const topicPerf = {};
|
|
139714
139741
|
history2.forEach((session) => {
|
|
139715
|
-
session.summary.topics.forEach(
|
|
139716
|
-
|
|
139717
|
-
if (
|
|
139718
|
-
|
|
139719
|
-
|
|
139720
|
-
|
|
139721
|
-
|
|
139722
|
-
|
|
139742
|
+
session.summary.topics.forEach(
|
|
139743
|
+
(topicInfo) => {
|
|
139744
|
+
if (session.summary.percentage !== null) {
|
|
139745
|
+
if (!subjectPerf[topicInfo.subject])
|
|
139746
|
+
subjectPerf[topicInfo.subject] = {
|
|
139747
|
+
total: 0,
|
|
139748
|
+
count: 0
|
|
139749
|
+
};
|
|
139750
|
+
subjectPerf[topicInfo.subject].total += session.summary.percentage;
|
|
139751
|
+
subjectPerf[topicInfo.subject].count++;
|
|
139752
|
+
if (!topicPerf[topicInfo.topic])
|
|
139753
|
+
topicPerf[topicInfo.topic] = { total: 0, count: 0 };
|
|
139754
|
+
topicPerf[topicInfo.topic].total += session.summary.percentage;
|
|
139755
|
+
topicPerf[topicInfo.topic].count++;
|
|
139756
|
+
}
|
|
139723
139757
|
}
|
|
139724
|
-
|
|
139758
|
+
);
|
|
139725
139759
|
});
|
|
139726
139760
|
const formatPerf = (perfData) => {
|
|
139727
139761
|
return Object.entries(perfData).map(([name3, data]) => ({
|
|
139728
139762
|
name: name3,
|
|
139729
139763
|
totalSessions: data.count,
|
|
139730
|
-
averageScore: parseFloat(
|
|
139764
|
+
averageScore: parseFloat(
|
|
139765
|
+
(data.total / data.count).toFixed(2)
|
|
139766
|
+
)
|
|
139731
139767
|
})).sort((a4, b2) => b2.totalSessions - a4.totalSessions);
|
|
139732
139768
|
};
|
|
139733
139769
|
return {
|
|
@@ -139745,6 +139781,8 @@ var PracticeHistoryService = class {
|
|
|
139745
139781
|
}
|
|
139746
139782
|
}
|
|
139747
139783
|
};
|
|
139784
|
+
// NEW: A static property to hold the injected sync provider
|
|
139785
|
+
PracticeHistoryService.syncProvider = null;
|
|
139748
139786
|
|
|
139749
139787
|
// src/services/RoadmapService.ts
|
|
139750
139788
|
init_react_shim();
|
|
@@ -167225,15 +167263,19 @@ var ClientTranslation = ({ tKey, options, fallback: fallback2 }) => {
|
|
|
167225
167263
|
};
|
|
167226
167264
|
|
|
167227
167265
|
// src/react-ui/components/app/PersonalPracticeDashboard.tsx
|
|
167228
|
-
var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
167266
|
+
var PersonalPracticeDashboard = ({ settingsPath, initialHistory, initialStats, isControlled = false }) => {
|
|
167229
167267
|
const router = navigation.useRouter();
|
|
167230
167268
|
const { toast: toast2 } = useToast();
|
|
167231
167269
|
const { t: t4, i18n } = useTranslation();
|
|
167232
167270
|
const [isMounted, setIsMounted] = React163.useState(false);
|
|
167233
167271
|
React163.useEffect(() => setIsMounted(true), []);
|
|
167234
167272
|
const [isLoading, setIsLoading] = React163.useState(true);
|
|
167235
|
-
const [stats, setStats] = React163.useState(
|
|
167236
|
-
|
|
167273
|
+
const [stats, setStats] = React163.useState(
|
|
167274
|
+
initialStats || null
|
|
167275
|
+
);
|
|
167276
|
+
const [history2, setHistory] = React163.useState(
|
|
167277
|
+
initialHistory || []
|
|
167278
|
+
);
|
|
167237
167279
|
const [userName, setUserName] = React163.useState(null);
|
|
167238
167280
|
const [allAchievements, setAllAchievements] = React163.useState([]);
|
|
167239
167281
|
const [motivationalQuote, setMotivationalQuote] = React163.useState(null);
|
|
@@ -167247,6 +167289,10 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
|
167247
167289
|
);
|
|
167248
167290
|
const [isSettingsModalOpen, setIsSettingsModalOpen] = React163.useState(false);
|
|
167249
167291
|
const loadDashboardData = React163.useCallback(() => {
|
|
167292
|
+
if (isControlled) {
|
|
167293
|
+
setIsLoading(false);
|
|
167294
|
+
return;
|
|
167295
|
+
}
|
|
167250
167296
|
setIsLoading(true);
|
|
167251
167297
|
setDashboardLayout(DashboardLayoutService.getLayout());
|
|
167252
167298
|
const practiceHistory = PracticeHistoryService.getPracticeHistory();
|
|
@@ -167275,10 +167321,20 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
|
167275
167321
|
if (KnowledgeCardService.getPendingConcepts().length > 0) {
|
|
167276
167322
|
CardGenerationProcessor.processQueue();
|
|
167277
167323
|
}
|
|
167278
|
-
}, [toast2, t4]);
|
|
167324
|
+
}, [isControlled, toast2, t4]);
|
|
167279
167325
|
React163.useEffect(() => {
|
|
167280
|
-
|
|
167281
|
-
|
|
167326
|
+
if (isControlled) {
|
|
167327
|
+
setHistory(initialHistory || []);
|
|
167328
|
+
setStats(initialStats || null);
|
|
167329
|
+
const name3 = UserConfigService.getFullName();
|
|
167330
|
+
setUserName(name3);
|
|
167331
|
+
const achievementsWithStatus = AchievementService.getAllAchievementsWithStatus();
|
|
167332
|
+
setAllAchievements(achievementsWithStatus);
|
|
167333
|
+
setIsLoading(false);
|
|
167334
|
+
} else {
|
|
167335
|
+
loadDashboardData();
|
|
167336
|
+
}
|
|
167337
|
+
}, [isControlled, initialHistory, initialStats, loadDashboardData]);
|
|
167282
167338
|
React163.useEffect(() => {
|
|
167283
167339
|
setMotivationalQuote(QuoteService.getRandomQuote());
|
|
167284
167340
|
const fetchAIQuote = async () => {
|
package/dist/react-ui.d.cts
CHANGED
|
@@ -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,
|
|
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,
|
|
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 };
|
package/dist/react-ui.d.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
@@ -139543,6 +139543,11 @@ AlertDialogCancel2.displayName = Cancel.displayName;
|
|
|
139543
139543
|
init_react_shim();
|
|
139544
139544
|
var LOCAL_STORAGE_KEY = "iqk_practice_history_v2";
|
|
139545
139545
|
var PracticeHistoryService = class {
|
|
139546
|
+
// NEW: A configuration method to allow the host app (QMS) to inject its sync logic
|
|
139547
|
+
static configure(provider) {
|
|
139548
|
+
this.syncProvider = provider.syncProvider;
|
|
139549
|
+
console.log("PracticeHistoryService configured with a sync provider.");
|
|
139550
|
+
}
|
|
139546
139551
|
static getPracticeHistory() {
|
|
139547
139552
|
if (typeof window === "undefined") return [];
|
|
139548
139553
|
try {
|
|
@@ -139556,20 +139561,30 @@ var PracticeHistoryService = class {
|
|
|
139556
139561
|
static saveHistory(history2) {
|
|
139557
139562
|
if (typeof window !== "undefined") {
|
|
139558
139563
|
try {
|
|
139559
|
-
localStorage.setItem(
|
|
139564
|
+
localStorage.setItem(
|
|
139565
|
+
LOCAL_STORAGE_KEY,
|
|
139566
|
+
JSON.stringify(history2)
|
|
139567
|
+
);
|
|
139560
139568
|
} catch (e3) {
|
|
139561
|
-
console.error(
|
|
139569
|
+
console.error(
|
|
139570
|
+
"Error saving practice history to localStorage:",
|
|
139571
|
+
e3
|
|
139572
|
+
);
|
|
139562
139573
|
}
|
|
139563
139574
|
}
|
|
139564
139575
|
}
|
|
139565
139576
|
static saveCompletedPracticeSession(quizConfig, result, review = null) {
|
|
139566
139577
|
const history2 = this.getPracticeHistory();
|
|
139567
139578
|
const topicsCovered = Array.from(
|
|
139568
|
-
new Set(
|
|
139569
|
-
|
|
139570
|
-
|
|
139571
|
-
|
|
139572
|
-
|
|
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
|
+
)
|
|
139573
139588
|
).map((s4) => JSON.parse(s4));
|
|
139574
139589
|
const newSession = {
|
|
139575
139590
|
id: `session_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`,
|
|
@@ -139586,6 +139601,14 @@ var PracticeHistoryService = class {
|
|
|
139586
139601
|
};
|
|
139587
139602
|
history2.unshift(newSession);
|
|
139588
139603
|
this.saveHistory(history2);
|
|
139604
|
+
if (this.syncProvider) {
|
|
139605
|
+
console.log(
|
|
139606
|
+
`Sync provider found. Pushing session ${newSession.id} to backend.`
|
|
139607
|
+
);
|
|
139608
|
+
this.syncProvider.pushSession(newSession).catch((err) => {
|
|
139609
|
+
console.error("Sync provider failed to push session:", err);
|
|
139610
|
+
});
|
|
139611
|
+
}
|
|
139589
139612
|
return newSession.id;
|
|
139590
139613
|
}
|
|
139591
139614
|
static updatePracticeReview(sessionId, review) {
|
|
@@ -139595,7 +139618,9 @@ var PracticeHistoryService = class {
|
|
|
139595
139618
|
history2[sessionIndex].quizReview = review;
|
|
139596
139619
|
this.saveHistory(history2);
|
|
139597
139620
|
} else {
|
|
139598
|
-
console.warn(
|
|
139621
|
+
console.warn(
|
|
139622
|
+
`Could not find session with ID "${sessionId}" to update review.`
|
|
139623
|
+
);
|
|
139599
139624
|
}
|
|
139600
139625
|
}
|
|
139601
139626
|
static getPracticeSessionById(sessionId) {
|
|
@@ -139653,7 +139678,9 @@ var PracticeHistoryService = class {
|
|
|
139653
139678
|
const currentDay = new Date(sortedDays[i2]);
|
|
139654
139679
|
const nextDay = new Date(sortedDays[i2 + 1]);
|
|
139655
139680
|
const diffTime = currentDay.getTime() - nextDay.getTime();
|
|
139656
|
-
const diffDays = Math.round(
|
|
139681
|
+
const diffDays = Math.round(
|
|
139682
|
+
diffTime / (1e3 * 60 * 60 * 24)
|
|
139683
|
+
);
|
|
139657
139684
|
if (diffDays === 1) {
|
|
139658
139685
|
currentStreak++;
|
|
139659
139686
|
} else {
|
|
@@ -139685,22 +139712,31 @@ var PracticeHistoryService = class {
|
|
|
139685
139712
|
const subjectPerf = {};
|
|
139686
139713
|
const topicPerf = {};
|
|
139687
139714
|
history2.forEach((session) => {
|
|
139688
|
-
session.summary.topics.forEach(
|
|
139689
|
-
|
|
139690
|
-
if (
|
|
139691
|
-
|
|
139692
|
-
|
|
139693
|
-
|
|
139694
|
-
|
|
139695
|
-
|
|
139715
|
+
session.summary.topics.forEach(
|
|
139716
|
+
(topicInfo) => {
|
|
139717
|
+
if (session.summary.percentage !== null) {
|
|
139718
|
+
if (!subjectPerf[topicInfo.subject])
|
|
139719
|
+
subjectPerf[topicInfo.subject] = {
|
|
139720
|
+
total: 0,
|
|
139721
|
+
count: 0
|
|
139722
|
+
};
|
|
139723
|
+
subjectPerf[topicInfo.subject].total += session.summary.percentage;
|
|
139724
|
+
subjectPerf[topicInfo.subject].count++;
|
|
139725
|
+
if (!topicPerf[topicInfo.topic])
|
|
139726
|
+
topicPerf[topicInfo.topic] = { total: 0, count: 0 };
|
|
139727
|
+
topicPerf[topicInfo.topic].total += session.summary.percentage;
|
|
139728
|
+
topicPerf[topicInfo.topic].count++;
|
|
139729
|
+
}
|
|
139696
139730
|
}
|
|
139697
|
-
|
|
139731
|
+
);
|
|
139698
139732
|
});
|
|
139699
139733
|
const formatPerf = (perfData) => {
|
|
139700
139734
|
return Object.entries(perfData).map(([name3, data]) => ({
|
|
139701
139735
|
name: name3,
|
|
139702
139736
|
totalSessions: data.count,
|
|
139703
|
-
averageScore: parseFloat(
|
|
139737
|
+
averageScore: parseFloat(
|
|
139738
|
+
(data.total / data.count).toFixed(2)
|
|
139739
|
+
)
|
|
139704
139740
|
})).sort((a4, b2) => b2.totalSessions - a4.totalSessions);
|
|
139705
139741
|
};
|
|
139706
139742
|
return {
|
|
@@ -139718,6 +139754,8 @@ var PracticeHistoryService = class {
|
|
|
139718
139754
|
}
|
|
139719
139755
|
}
|
|
139720
139756
|
};
|
|
139757
|
+
// NEW: A static property to hold the injected sync provider
|
|
139758
|
+
PracticeHistoryService.syncProvider = null;
|
|
139721
139759
|
|
|
139722
139760
|
// src/services/RoadmapService.ts
|
|
139723
139761
|
init_react_shim();
|
|
@@ -167198,15 +167236,19 @@ var ClientTranslation = ({ tKey, options, fallback: fallback2 }) => {
|
|
|
167198
167236
|
};
|
|
167199
167237
|
|
|
167200
167238
|
// src/react-ui/components/app/PersonalPracticeDashboard.tsx
|
|
167201
|
-
var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
167239
|
+
var PersonalPracticeDashboard = ({ settingsPath, initialHistory, initialStats, isControlled = false }) => {
|
|
167202
167240
|
const router = useRouter();
|
|
167203
167241
|
const { toast: toast2 } = useToast();
|
|
167204
167242
|
const { t: t4, i18n } = useTranslation();
|
|
167205
167243
|
const [isMounted, setIsMounted] = useState(false);
|
|
167206
167244
|
useEffect(() => setIsMounted(true), []);
|
|
167207
167245
|
const [isLoading, setIsLoading] = useState(true);
|
|
167208
|
-
const [stats, setStats] = useState(
|
|
167209
|
-
|
|
167246
|
+
const [stats, setStats] = useState(
|
|
167247
|
+
initialStats || null
|
|
167248
|
+
);
|
|
167249
|
+
const [history2, setHistory] = useState(
|
|
167250
|
+
initialHistory || []
|
|
167251
|
+
);
|
|
167210
167252
|
const [userName, setUserName] = useState(null);
|
|
167211
167253
|
const [allAchievements, setAllAchievements] = useState([]);
|
|
167212
167254
|
const [motivationalQuote, setMotivationalQuote] = useState(null);
|
|
@@ -167220,6 +167262,10 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
|
167220
167262
|
);
|
|
167221
167263
|
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
|
|
167222
167264
|
const loadDashboardData = useCallback(() => {
|
|
167265
|
+
if (isControlled) {
|
|
167266
|
+
setIsLoading(false);
|
|
167267
|
+
return;
|
|
167268
|
+
}
|
|
167223
167269
|
setIsLoading(true);
|
|
167224
167270
|
setDashboardLayout(DashboardLayoutService.getLayout());
|
|
167225
167271
|
const practiceHistory = PracticeHistoryService.getPracticeHistory();
|
|
@@ -167248,10 +167294,20 @@ var PersonalPracticeDashboard = ({ settingsPath }) => {
|
|
|
167248
167294
|
if (KnowledgeCardService.getPendingConcepts().length > 0) {
|
|
167249
167295
|
CardGenerationProcessor.processQueue();
|
|
167250
167296
|
}
|
|
167251
|
-
}, [toast2, t4]);
|
|
167297
|
+
}, [isControlled, toast2, t4]);
|
|
167252
167298
|
useEffect(() => {
|
|
167253
|
-
|
|
167254
|
-
|
|
167299
|
+
if (isControlled) {
|
|
167300
|
+
setHistory(initialHistory || []);
|
|
167301
|
+
setStats(initialStats || null);
|
|
167302
|
+
const name3 = UserConfigService.getFullName();
|
|
167303
|
+
setUserName(name3);
|
|
167304
|
+
const achievementsWithStatus = AchievementService.getAllAchievementsWithStatus();
|
|
167305
|
+
setAllAchievements(achievementsWithStatus);
|
|
167306
|
+
setIsLoading(false);
|
|
167307
|
+
} else {
|
|
167308
|
+
loadDashboardData();
|
|
167309
|
+
}
|
|
167310
|
+
}, [isControlled, initialHistory, initialStats, loadDashboardData]);
|
|
167255
167311
|
useEffect(() => {
|
|
167256
167312
|
setMotivationalQuote(QuoteService.getRandomQuote());
|
|
167257
167313
|
const fetchAIQuote = async () => {
|
package/package.json
CHANGED