fix: 完善个人中心卡片管理
This commit is contained in:
@@ -136,6 +136,16 @@ def mark_help_card_copied(
|
|||||||
return api_success(help_card_dict(HelpCardService.mark_copied(db, user=current_user, card_id=card_id)))
|
return api_success(help_card_dict(HelpCardService.mark_copied(db, user=current_user, card_id=card_id)))
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/help-card/{card_id}")
|
||||||
|
def delete_help_card(
|
||||||
|
card_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
) -> dict:
|
||||||
|
HelpCardService.delete(db, user=current_user, card_id=card_id)
|
||||||
|
return api_success()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/session/{session_id}/share-draft")
|
@router.post("/session/{session_id}/share-draft")
|
||||||
def generate_share_draft(
|
def generate_share_draft(
|
||||||
session_id: int,
|
session_id: int,
|
||||||
@@ -165,6 +175,16 @@ def mark_share_draft_copied(
|
|||||||
return api_success(share_draft_dict(ShareDraftService.mark_copied(db, user=current_user, draft_id=draft_id)))
|
return api_success(share_draft_dict(ShareDraftService.mark_copied(db, user=current_user, draft_id=draft_id)))
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/share-draft/{draft_id}")
|
||||||
|
def delete_share_draft(
|
||||||
|
draft_id: int,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
) -> dict:
|
||||||
|
ShareDraftService.delete(db, user=current_user, draft_id=draft_id)
|
||||||
|
return api_success()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/completions")
|
@router.post("/completions")
|
||||||
def completions(
|
def completions(
|
||||||
payload: ChatCompletionRequest,
|
payload: ChatCompletionRequest,
|
||||||
|
|||||||
@@ -62,6 +62,14 @@ class HelpCardService:
|
|||||||
db.refresh(card)
|
db.refresh(card)
|
||||||
return card
|
return card
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete(db: Session, *, user: User, card_id: int) -> None:
|
||||||
|
card = db.scalar(select(TeacherHelpCard).where(TeacherHelpCard.id == card_id, TeacherHelpCard.user_id == user.id))
|
||||||
|
if card is None:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="求助卡不存在")
|
||||||
|
db.delete(card)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
def help_card_dict(card: TeacherHelpCard) -> dict:
|
def help_card_dict(card: TeacherHelpCard) -> dict:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ class ShareDraftService:
|
|||||||
db.refresh(draft)
|
db.refresh(draft)
|
||||||
return draft
|
return draft
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete(db: Session, *, user: User, draft_id: int) -> None:
|
||||||
|
draft = db.scalar(select(ShareDraft).where(ShareDraft.id == draft_id, ShareDraft.user_id == user.id))
|
||||||
|
if draft is None:
|
||||||
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="分享稿不存在")
|
||||||
|
db.delete(draft)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
def share_draft_dict(draft: ShareDraft) -> dict:
|
def share_draft_dict(draft: ShareDraft) -> dict:
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import HTTPException
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy.pool import StaticPool
|
from sqlalchemy.pool import StaticPool
|
||||||
@@ -63,3 +65,14 @@ def test_generate_help_card_from_topic_summary_and_mark_copied():
|
|||||||
|
|
||||||
assert copied.copied == 1
|
assert copied.copied == 1
|
||||||
assert copied.copied_at is not None
|
assert copied.copied_at is not None
|
||||||
|
|
||||||
|
other_user = User(id=2, phone="13800000002", name="其他学员", daily_chat_limit=100, daily_chat_used=0)
|
||||||
|
db.add(other_user)
|
||||||
|
db.commit()
|
||||||
|
with pytest.raises(HTTPException) as error:
|
||||||
|
HelpCardService.delete(db, user=other_user, card_id=card.id)
|
||||||
|
assert error.value.status_code == 404
|
||||||
|
assert db.get(TeacherHelpCard, card.id) is not None
|
||||||
|
|
||||||
|
HelpCardService.delete(db, user=user, card_id=card.id)
|
||||||
|
assert db.get(TeacherHelpCard, card.id) is None
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import HTTPException
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy.pool import StaticPool
|
from sqlalchemy.pool import StaticPool
|
||||||
@@ -65,3 +67,14 @@ def test_generate_share_draft_from_topic_summary_and_mark_copied():
|
|||||||
|
|
||||||
assert copied.copied == 1
|
assert copied.copied == 1
|
||||||
assert copied.copied_at is not None
|
assert copied.copied_at is not None
|
||||||
|
|
||||||
|
other_user = User(id=2, phone="13800000002", name="其他学员", daily_chat_limit=100, daily_chat_used=0)
|
||||||
|
db.add(other_user)
|
||||||
|
db.commit()
|
||||||
|
with pytest.raises(HTTPException) as error:
|
||||||
|
ShareDraftService.delete(db, user=other_user, draft_id=draft.id)
|
||||||
|
assert error.value.status_code == 404
|
||||||
|
assert db.get(ShareDraft, draft.id) is not None
|
||||||
|
|
||||||
|
ShareDraftService.delete(db, user=user, draft_id=draft.id)
|
||||||
|
assert db.get(ShareDraft, draft.id) is None
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const helpCardHistory = ref<TeacherHelpCard[]>([]);
|
|||||||
const shareDraftHistory = ref<ShareDraft[]>([]);
|
const shareDraftHistory = ref<ShareDraft[]>([]);
|
||||||
const reportHistory = ref<PeriodicReport[]>([]);
|
const reportHistory = ref<PeriodicReport[]>([]);
|
||||||
const personalCenterLoading = ref(false);
|
const personalCenterLoading = ref(false);
|
||||||
|
const cardOperationPending = ref(false);
|
||||||
const finishingTopic = ref(false);
|
const finishingTopic = ref(false);
|
||||||
const generatingHelpCard = ref(false);
|
const generatingHelpCard = ref(false);
|
||||||
const generatingShareDraft = ref(false);
|
const generatingShareDraft = ref(false);
|
||||||
@@ -322,6 +323,62 @@ async function copyShareDraft() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function copyHistoryHelpCard(card: TeacherHelpCard) {
|
||||||
|
try {
|
||||||
|
await copyText(card.content);
|
||||||
|
const updated = await api.markHelpCardCopied(card.id);
|
||||||
|
helpCardHistory.value = helpCardHistory.value.map((item) => item.id === updated.id ? updated : item);
|
||||||
|
showToast("求助卡已复制,可再次粘贴给老师");
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "复制失败,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyHistoryShareDraft(draft: ShareDraft) {
|
||||||
|
try {
|
||||||
|
await copyText(draft.content);
|
||||||
|
const updated = await api.markShareDraftCopied(draft.id);
|
||||||
|
shareDraftHistory.value = shareDraftHistory.value.map((item) => item.id === updated.id ? updated : item);
|
||||||
|
showToast("分享稿已复制,可再次粘贴到班级群");
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "复制失败,请稍后重试");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteHistoryHelpCard(cardId: number, done: (success: boolean) => void) {
|
||||||
|
if (cardOperationPending.value) return;
|
||||||
|
cardOperationPending.value = true;
|
||||||
|
try {
|
||||||
|
await api.deleteHelpCard(cardId);
|
||||||
|
helpCardHistory.value = helpCardHistory.value.filter((item) => item.id !== cardId);
|
||||||
|
if (helpCard.value?.id === cardId) helpCard.value = null;
|
||||||
|
showToast("求助卡已删除");
|
||||||
|
done(true);
|
||||||
|
} catch (error) {
|
||||||
|
done(false);
|
||||||
|
handleError(error, "求助卡删除失败");
|
||||||
|
} finally {
|
||||||
|
cardOperationPending.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteHistoryShareDraft(draftId: number, done: (success: boolean) => void) {
|
||||||
|
if (cardOperationPending.value) return;
|
||||||
|
cardOperationPending.value = true;
|
||||||
|
try {
|
||||||
|
await api.deleteShareDraft(draftId);
|
||||||
|
shareDraftHistory.value = shareDraftHistory.value.filter((item) => item.id !== draftId);
|
||||||
|
if (shareDraft.value?.id === draftId) shareDraft.value = null;
|
||||||
|
showToast("分享稿已删除");
|
||||||
|
done(true);
|
||||||
|
} catch (error) {
|
||||||
|
done(false);
|
||||||
|
handleError(error, "分享稿删除失败");
|
||||||
|
} finally {
|
||||||
|
cardOperationPending.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function openPersonalCenter(section: "overview" | "review" | "records" = "overview") {
|
async function openPersonalCenter(section: "overview" | "review" | "records" = "overview") {
|
||||||
personalCenterSection.value = section;
|
personalCenterSection.value = section;
|
||||||
personalCenterOpen.value = true;
|
personalCenterOpen.value = true;
|
||||||
@@ -522,8 +579,13 @@ async function copyText(text: string) {
|
|||||||
:reports="reportHistory"
|
:reports="reportHistory"
|
||||||
:help-cards="helpCardHistory"
|
:help-cards="helpCardHistory"
|
||||||
:share-drafts="shareDraftHistory"
|
:share-drafts="shareDraftHistory"
|
||||||
|
:operation-pending="cardOperationPending"
|
||||||
@close="personalCenterOpen = false"
|
@close="personalCenterOpen = false"
|
||||||
@logout="personalCenterOpen = false; logoutDialogOpen = true"
|
@logout="personalCenterOpen = false; logoutDialogOpen = true"
|
||||||
|
@copy-help-card="copyHistoryHelpCard"
|
||||||
|
@copy-share-draft="copyHistoryShareDraft"
|
||||||
|
@delete-help-card="deleteHistoryHelpCard"
|
||||||
|
@delete-share-draft="deleteHistoryShareDraft"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AppDialog v-if="helpCardDialogOpen" title="老师求助卡" labelled-by="help-card-title" @close="helpCardDialogOpen = false">
|
<AppDialog v-if="helpCardDialogOpen" title="老师求助卡" labelled-by="help-card-title" @close="helpCardDialogOpen = false">
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import {
|
|||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
CircleMinus,
|
CircleMinus,
|
||||||
|
Copy,
|
||||||
LifeBuoy,
|
LifeBuoy,
|
||||||
LogOut,
|
LogOut,
|
||||||
PackageCheck,
|
PackageCheck,
|
||||||
Share2,
|
Share2,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
|
Trash2,
|
||||||
} from "@lucide/vue";
|
} from "@lucide/vue";
|
||||||
import { computed, ref } from "vue";
|
import { computed, ref } from "vue";
|
||||||
|
|
||||||
@@ -26,15 +28,21 @@ const props = defineProps<{
|
|||||||
reports: PeriodicReport[];
|
reports: PeriodicReport[];
|
||||||
helpCards: TeacherHelpCard[];
|
helpCards: TeacherHelpCard[];
|
||||||
shareDrafts: ShareDraft[];
|
shareDrafts: ShareDraft[];
|
||||||
|
operationPending: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: [];
|
close: [];
|
||||||
logout: [];
|
logout: [];
|
||||||
|
copyHelpCard: [card: TeacherHelpCard];
|
||||||
|
copyShareDraft: [draft: ShareDraft];
|
||||||
|
deleteHelpCard: [cardId: number, done: (success: boolean) => void];
|
||||||
|
deleteShareDraft: [draftId: number, done: (success: boolean) => void];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const activeSection = ref<CenterSection>(props.initialSection);
|
const activeSection = ref<CenterSection>(props.initialSection);
|
||||||
const activeCardSection = ref<CardSection>("help");
|
const activeCardSection = ref<CardSection>("help");
|
||||||
|
const deletingCard = ref<{ type: CardSection; id: number } | null>(null);
|
||||||
const entitlement = computed(() => props.user.entitlement);
|
const entitlement = computed(() => props.user.entitlement);
|
||||||
const displayName = computed(() => props.user.nickname?.trim() || props.user.name);
|
const displayName = computed(() => props.user.nickname?.trim() || props.user.name);
|
||||||
const nameInitial = computed(() => displayName.value.slice(0, 1));
|
const nameInitial = computed(() => displayName.value.slice(0, 1));
|
||||||
@@ -75,6 +83,19 @@ function settlementStatusLabel(status: string) {
|
|||||||
failed: "整理失败",
|
failed: "整理失败",
|
||||||
}[status] || status;
|
}[status] || status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function submitDeleteCard() {
|
||||||
|
if (!deletingCard.value || props.operationPending) return;
|
||||||
|
const target = deletingCard.value;
|
||||||
|
const done = (success: boolean) => {
|
||||||
|
if (success) deletingCard.value = null;
|
||||||
|
};
|
||||||
|
if (target.type === "help") {
|
||||||
|
emit("deleteHelpCard", target.id, done);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit("deleteShareDraft", target.id, done);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -247,6 +268,16 @@ function settlementStatusLabel(status: string) {
|
|||||||
</div>
|
</div>
|
||||||
<ChevronDown :size="17" aria-hidden="true" />
|
<ChevronDown :size="17" aria-hidden="true" />
|
||||||
</summary>
|
</summary>
|
||||||
|
<div class="card-history-actions">
|
||||||
|
<button type="button" class="copy" @click="emit('copyHelpCard', item)">
|
||||||
|
<Copy :size="15" aria-hidden="true" />
|
||||||
|
{{ item.copied ? "再次复制" : "复制卡片" }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="delete" @click="deletingCard = { type: 'help', id: item.id }">
|
||||||
|
<Trash2 :size="15" aria-hidden="true" />
|
||||||
|
删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<pre>{{ item.content }}</pre>
|
<pre>{{ item.content }}</pre>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
@@ -276,6 +307,16 @@ function settlementStatusLabel(status: string) {
|
|||||||
</div>
|
</div>
|
||||||
<ChevronDown :size="17" aria-hidden="true" />
|
<ChevronDown :size="17" aria-hidden="true" />
|
||||||
</summary>
|
</summary>
|
||||||
|
<div class="card-history-actions">
|
||||||
|
<button type="button" class="copy" @click="emit('copyShareDraft', item)">
|
||||||
|
<Copy :size="15" aria-hidden="true" />
|
||||||
|
{{ item.copied ? "再次复制" : "复制卡片" }}
|
||||||
|
</button>
|
||||||
|
<button type="button" class="delete" @click="deletingCard = { type: 'share', id: item.id }">
|
||||||
|
<Trash2 :size="15" aria-hidden="true" />
|
||||||
|
删除
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<pre>{{ item.content }}</pre>
|
<pre>{{ item.content }}</pre>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
@@ -294,6 +335,25 @@ function settlementStatusLabel(status: string) {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</AppDialog>
|
</AppDialog>
|
||||||
|
|
||||||
|
<AppDialog
|
||||||
|
v-if="deletingCard"
|
||||||
|
title="删除卡片"
|
||||||
|
labelled-by="delete-card-title"
|
||||||
|
@close="deletingCard = null"
|
||||||
|
>
|
||||||
|
<p class="confirm-copy">
|
||||||
|
确定删除这张{{ deletingCard.type === "help" ? "老师求助卡" : "班级分享稿" }}吗?删除后无法恢复。
|
||||||
|
</p>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<button type="button" class="dialog-secondary" :disabled="operationPending" @click="deletingCard = null">取消</button>
|
||||||
|
<button type="button" class="dialog-danger" :disabled="operationPending" @click="submitDeleteCard">
|
||||||
|
{{ operationPending ? "删除中..." : "删除" }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</AppDialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -349,7 +409,7 @@ function settlementStatusLabel(status: string) {
|
|||||||
.report-record > time { display: block; padding: 0 12px 6px; }
|
.report-record > time { display: block; padding: 0 12px 6px; }
|
||||||
.report-record pre { max-height: 260px; overflow: auto; margin: 0; padding: 11px 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
.report-record pre { max-height: 260px; overflow: auto; margin: 0; padding: 11px 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
||||||
.records-section { align-content: start; }
|
.records-section { align-content: start; }
|
||||||
.card-type-tabs { position: sticky; top: -1px; z-index: 2; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 2px 0 4px; background: #fff; }
|
.card-type-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 2px 0 4px; background: #fff; }
|
||||||
.card-type-tabs button { min-width: 0; min-height: 52px; display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; align-items: center; gap: 7px; padding: 0 11px; border: 1px solid var(--chat-border); border-radius: 13px; background: #fff; color: var(--chat-muted); font-size: 12px; font-weight: 700; text-align: left; }
|
.card-type-tabs button { min-width: 0; min-height: 52px; display: grid; grid-template-columns: 20px minmax(0, 1fr) auto; align-items: center; gap: 7px; padding: 0 11px; border: 1px solid var(--chat-border); border-radius: 13px; background: #fff; color: var(--chat-muted); font-size: 12px; font-weight: 700; text-align: left; }
|
||||||
.card-type-tabs button[aria-selected="true"] { border-color: rgba(20, 148, 119, 0.42); background: var(--chat-brand-soft); color: var(--chat-brand-dark); box-shadow: 0 5px 16px rgba(27, 68, 55, 0.07); }
|
.card-type-tabs button[aria-selected="true"] { border-color: rgba(20, 148, 119, 0.42); background: var(--chat-brand-soft); color: var(--chat-brand-dark); box-shadow: 0 5px 16px rgba(27, 68, 55, 0.07); }
|
||||||
.card-type-tabs button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.card-type-tabs button span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
@@ -372,6 +432,10 @@ function settlementStatusLabel(status: string) {
|
|||||||
.card-history-item summary > svg { transition: transform 0.18s ease; }
|
.card-history-item summary > svg { transition: transform 0.18s ease; }
|
||||||
.card-history-item[open] summary > svg { transform: rotate(180deg); }
|
.card-history-item[open] summary > svg { transform: rotate(180deg); }
|
||||||
.card-history-item pre { max-height: 300px; overflow: auto; margin: 0; padding: 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
.card-history-item pre { max-height: 300px; overflow: auto; margin: 0; padding: 12px; border-top: 1px solid var(--chat-border); background: #fbfcfc; color: var(--chat-muted); font: inherit; font-size: 12px; line-height: 1.7; white-space: pre-wrap; }
|
||||||
|
.card-history-actions { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; padding: 9px 10px 10px; border-top: 1px solid var(--chat-border); background: #fff; }
|
||||||
|
.card-history-actions button { min-height: 38px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 13px; border: 0; border-radius: 10px; font-size: 12px; font-weight: 700; }
|
||||||
|
.card-history-actions .copy { background: var(--chat-brand-soft); color: var(--chat-brand-dark); }
|
||||||
|
.card-history-actions .delete { background: #fff2f1; color: var(--chat-danger); }
|
||||||
.center-empty { margin: 0; padding: 22px 14px; border-radius: 14px; background: #f6f8f7; color: var(--chat-muted); font-size: 13px; line-height: 1.7; text-align: center; }
|
.center-empty { margin: 0; padding: 22px 14px; border-radius: 14px; background: #f6f8f7; color: var(--chat-muted); font-size: 13px; line-height: 1.7; text-align: center; }
|
||||||
.center-empty.small { padding: 14px; font-size: 12px; }
|
.center-empty.small { padding: 14px; font-size: 12px; }
|
||||||
.personal-center-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
.personal-center-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
||||||
|
|||||||
@@ -93,9 +93,11 @@ export const api = {
|
|||||||
generateHelpCard: (sessionId: number) => request<TeacherHelpCard>(`/chat/session/${sessionId}/help-card`, { method: "POST", body: JSON.stringify({}) }),
|
generateHelpCard: (sessionId: number) => request<TeacherHelpCard>(`/chat/session/${sessionId}/help-card`, { method: "POST", body: JSON.stringify({}) }),
|
||||||
markHelpCardCopied: (cardId: number) => request<TeacherHelpCard>(`/chat/help-card/${cardId}/copied`, { method: "POST", body: JSON.stringify({}) }),
|
markHelpCardCopied: (cardId: number) => request<TeacherHelpCard>(`/chat/help-card/${cardId}/copied`, { method: "POST", body: JSON.stringify({}) }),
|
||||||
helpCards: (limit = 20) => request<TeacherHelpCard[]>(`/chat/help-card/list?limit=${limit}`),
|
helpCards: (limit = 20) => request<TeacherHelpCard[]>(`/chat/help-card/list?limit=${limit}`),
|
||||||
|
deleteHelpCard: (cardId: number) => request<null>(`/chat/help-card/${cardId}`, { method: "DELETE" }),
|
||||||
generateShareDraft: (sessionId: number) => request<ShareDraft>(`/chat/session/${sessionId}/share-draft`, { method: "POST", body: JSON.stringify({}) }),
|
generateShareDraft: (sessionId: number) => request<ShareDraft>(`/chat/session/${sessionId}/share-draft`, { method: "POST", body: JSON.stringify({}) }),
|
||||||
markShareDraftCopied: (draftId: number) => request<ShareDraft>(`/chat/share-draft/${draftId}/copied`, { method: "POST", body: JSON.stringify({}) }),
|
markShareDraftCopied: (draftId: number) => request<ShareDraft>(`/chat/share-draft/${draftId}/copied`, { method: "POST", body: JSON.stringify({}) }),
|
||||||
shareDrafts: (limit = 20) => request<ShareDraft[]>(`/chat/share-draft/list?limit=${limit}`),
|
shareDrafts: (limit = 20) => request<ShareDraft[]>(`/chat/share-draft/list?limit=${limit}`),
|
||||||
|
deleteShareDraft: (draftId: number) => request<null>(`/chat/share-draft/${draftId}`, { method: "DELETE" }),
|
||||||
practiceReview: () => request<PracticeReviewResult>("/user/growth-profile"),
|
practiceReview: () => request<PracticeReviewResult>("/user/growth-profile"),
|
||||||
periodicReports: (limit = 10) => request<PeriodicReport[]>(`/user/periodic-report/list?limit=${limit}`),
|
periodicReports: (limit = 10) => request<PeriodicReport[]>(`/user/periodic-report/list?limit=${limit}`),
|
||||||
stop: (sessionId: number) => request<null>("/chat/stop", { method: "POST", body: JSON.stringify({ sessionId }) }),
|
stop: (sessionId: number) => request<null>("/chat/stop", { method: "POST", body: JSON.stringify({ sessionId }) }),
|
||||||
|
|||||||
Reference in New Issue
Block a user