Skip to content

Commit 736ff1e

Browse files
authored
Merge pull request #2361 from themeum/refactor-dashboard-qna
QnA Refactored and edit delete options added
2 parents 7a1ad51 + 2ed8905 commit 736ff1e

24 files changed

Lines changed: 1281 additions & 619 deletions

File tree

assets/core/scss/components/_tooltip.scss

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,18 @@
2121
@include tutor-typography('tiny', 'regular', 'primary-inverse');
2222
position: fixed;
2323
z-index: 1070;
24-
max-width: 200px;
24+
max-width: 180px;
2525
padding: $tutor-spacing-4;
2626
background-color: $tutor-surface-dark;
2727
border-radius: $tutor-radius-sm;
2828
box-shadow: $tutor-shadow-md;
2929
word-wrap: break-word;
30-
text-align: start;
30+
text-align: center;
3131

3232
&-large {
3333
max-width: 320px;
3434
padding: $tutor-spacing-5;
35+
text-align: start;
3536
}
3637

3738
&-arrow-start {

assets/core/scss/mixins/_buttons.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@
166166
color: $tutor-text-primary;
167167

168168
svg {
169-
color: $tutor-icon-idle;
169+
color: $tutor-icon-secondary;
170170
}
171171

172172
&:hover:not(:disabled) {

assets/core/ts/services/Query.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -232,21 +232,16 @@ export class QueryService {
232232

233233
return result;
234234
} catch (err) {
235-
const error = {
236-
message: (err as Error).message || 'Mutation failed',
237-
code: (err as { code?: string }).code,
238-
} as TError;
239-
240-
(this as unknown as MutationState<TData, TVariables, TError>).error = error;
235+
(this as unknown as MutationState<TData, TVariables, TError>).error = err as TError;
241236
(this as unknown as MutationState<TData, TVariables, TError>).isError = true;
242237

243238
if (options.onError) {
244-
options.onError(error, variables);
239+
options.onError(err as TError, variables);
245240
}
246241

247242
// onSettled callback - always called
248243
if (options.onSettled) {
249-
options.onSettled(null, error, variables);
244+
options.onSettled(null, err as TError, variables);
250245
}
251246

252247
throw err;

assets/icons/read.svg

Lines changed: 3 additions & 0 deletions
Loading

assets/src/js/frontend/dashboard/pages/discussions.ts

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,21 @@ interface ReplyQnAPayload {
2626
question_id: number;
2727
answer: string;
2828
}
29+
interface UpdateQnAPayload {
30+
question_id: number;
31+
answer: string;
32+
}
33+
34+
interface DeleteQnAPayload {
35+
question_id: number;
36+
context?: 'question' | 'reply';
37+
}
2938

3039
const FORM_ID_PREFIXES = {
3140
COMMENT_EDIT: 'lesson-comment-edit-',
3241
COMMENT_REPLY: 'lesson-comment-reply-form-',
42+
QNA_EDIT: 'qna-edit-',
43+
QNA_REPLY: 'qna-reply-form-',
3344
};
3445

3546
const MODALS = {
@@ -39,10 +50,12 @@ const MODALS = {
3950

4051
const ELEMENT_IDS = {
4152
COMMENT_TEXT_PREFIX: 'tutor-lesson-comment-text-',
53+
QNA_TEXT_PREFIX: 'tutor-qna-text-',
4254
REPLIES_LIST_CONTAINER: 'tutor-discussion-replies-list',
4355
};
4456

4557
const URL_PARAMS = {
58+
TAB: 'tab',
4659
ID: 'id',
4760
ORDER: 'order',
4861
};
@@ -65,6 +78,8 @@ const discussionsPage = () => {
6578
qnaSingleActionMutation: null as MutationState<unknown, unknown> | null,
6679
deleteQnAMutation: null as MutationState<unknown, unknown> | null,
6780
replyQnAMutation: null as MutationState<unknown, unknown> | null,
81+
updateQnAMutation: null as MutationState<unknown, UpdateQnAPayload> | null,
82+
loadQnARepliesMutation: null as MutationState<unknown, unknown> | null,
6883
currentAction: null as string | null,
6984
currentQuestionId: null as number | null,
7085
isSolved: false,
@@ -139,7 +154,7 @@ const discussionsPage = () => {
139154

140155
// Q&A single action mutation (read, unread, solved, important, archived).
141156
this.qnaSingleActionMutation = this.query.useMutation(this.qnaSingleAction, {
142-
onSuccess: (response, payload: QnASingleActionPayload) => {
157+
onSuccess: (_, payload: QnASingleActionPayload) => {
143158
const action = payload.qna_action;
144159
if (action === 'solved') {
145160
this.isSolved = !this.isSolved;
@@ -162,10 +177,16 @@ const discussionsPage = () => {
162177

163178
// Q&A delete mutation.
164179
this.deleteQnAMutation = this.query.useMutation(this.deleteQnA, {
165-
onSuccess: () => {
166-
const url = new URL(window.location.href);
167-
url.searchParams.delete(URL_PARAMS.ID);
168-
window.location.href = url.toString();
180+
onSuccess: (_, payload) => {
181+
if (payload.context === 'reply') {
182+
toast.success(__('Reply deleted successfully', 'tutor'));
183+
modal.closeModal(MODALS.QNA_DELETE);
184+
this.reloadReplies();
185+
} else {
186+
const url = new URL(window.location.href);
187+
url.searchParams.delete(URL_PARAMS.ID);
188+
window.location.href = url.toString();
189+
}
169190
},
170191
onError: (error: Error) => {
171192
toast.error(convertToErrorMessage(error));
@@ -174,9 +195,33 @@ const discussionsPage = () => {
174195

175196
// Q&A reply mutation.
176197
this.replyQnAMutation = this.query.useMutation(this.replyQnA, {
177-
onSuccess: () => {
198+
onSuccess: (_, payload) => {
178199
toast.success(__('Reply saved successfully', 'tutor'));
179-
window.location.reload();
200+
this.reloadReplies();
201+
const formId = `${FORM_ID_PREFIXES.QNA_REPLY}${payload.question_id}`;
202+
if (form.hasForm(formId)) {
203+
form.reset(formId);
204+
}
205+
},
206+
onError: (error: Error) => {
207+
toast.error(convertToErrorMessage(error));
208+
},
209+
});
210+
211+
// Q&A update mutation.
212+
this.updateQnAMutation = this.query.useMutation(this.updateQnA, {
213+
onSuccess: (_, payload) => {
214+
toast.success(__('Updated successfully', 'tutor'));
215+
216+
// Update DOM directly for immediate feedback
217+
const element = document.getElementById(`${ELEMENT_IDS.QNA_TEXT_PREFIX}${payload.question_id}`);
218+
if (element) {
219+
element.innerHTML = payload.answer;
220+
}
221+
222+
if (this.editingId === payload.question_id) {
223+
this.setEditing(null);
224+
}
180225
},
181226
onError: (error: Error) => {
182227
toast.error(convertToErrorMessage(error));
@@ -191,12 +236,14 @@ const discussionsPage = () => {
191236

192237
const url = new URL(window.location.href);
193238
const commentId = parseInt(url.searchParams.get(URL_PARAMS.ID) || '0');
239+
const tab = url.searchParams.get(URL_PARAMS.TAB) || 'qna';
194240

195241
if (!commentId) return;
196242

197243
this.loadingReplies = true;
198244
try {
199-
const response = await wpAjaxInstance.post(endpoints.LOAD_DISCUSSION_REPLIES, {
245+
const endpoint = tab === 'qna' ? endpoints.LOAD_QNA_REPLIES : endpoints.LOAD_COMMENT_REPLIES;
246+
const response = await wpAjaxInstance.post(endpoint, {
200247
comment_id: commentId,
201248
order: this.repliesOrder,
202249
});
@@ -234,14 +281,18 @@ const discussionsPage = () => {
234281
return wpAjaxInstance.post(endpoints.QNA_SINGLE_ACTION, payload);
235282
},
236283

237-
deleteQnA(payload: { question_id: number }) {
284+
deleteQnA(payload: DeleteQnAPayload) {
238285
return wpAjaxInstance.post(endpoints.DELETE_DASHBOARD_QNA, payload);
239286
},
240287

241288
replyQnA(payload: ReplyQnAPayload) {
242289
return wpAjaxInstance.post(endpoints.CREATE_UPDATE_QNA, payload);
243290
},
244291

292+
updateQnA(payload: UpdateQnAPayload) {
293+
return wpAjaxInstance.post(endpoints.UPDATE_QNA, payload);
294+
},
295+
245296
async handleQnASingleAction(questionId: number, action: string, extras: Record<string, string> = {}) {
246297
this.currentAction = action;
247298
this.currentQuestionId = questionId;
@@ -278,15 +329,18 @@ const discussionsPage = () => {
278329
}
279330
},
280331

281-
setEditing(id: number | null) {
332+
setEditing(id: number | null, context = 'comment') {
333+
const prefix = context === 'qna' ? FORM_ID_PREFIXES.QNA_EDIT : FORM_ID_PREFIXES.COMMENT_EDIT;
334+
const field = context === 'qna' ? 'answer' : 'comment';
335+
282336
this.editingId = id;
283-
const formId = id ? `${FORM_ID_PREFIXES.COMMENT_EDIT}${id}` : null;
337+
const formId = id ? `${prefix}${id}` : null;
284338
this.editingFormId = formId;
285339

286340
if (id && formId) {
287341
this.$nextTick?.(() => {
288342
if (form.hasForm(formId)) {
289-
form.setFocus(formId, 'comment');
343+
form.setFocus(formId, field);
290344
}
291345
});
292346
}

0 commit comments

Comments
 (0)