-
Hangzhi Yu authoredHangzhi Yu authored
LessonSummary.vue 8.18 KiB
<template>
<div>
<!-- compact -->
<div
class="d-flex flex-column flex-md-row align-stretch align-md-center gap justify-start fill-height"
v-if="compact"
>
<documentation-compact-details
v-bind="dialogActivator.attrs"
v-on="dialogActivator.on"
v-if="
!documentation.canEdit &&
(documentation.topic ||
documentation.homework ||
documentation.groupNote)
"
:documentation="documentation"
@click="$emit('open')"
:class="{
'flex-grow-1 min-width pa-1 read-only-grid': true,
'full-width': $vuetify.breakpoint.mobile,
}"
/>
<v-alert
v-else-if="documentation.futureNotice"
type="warning"
outlined
class="min-width flex-grow-1 mb-0"
>
{{ $t("alsijil.coursebook.notices.future") }}
</v-alert>
<v-alert
v-else-if="!documentation.canEdit"
type="info"
outlined
class="min-width flex-grow-1 mb-0"
>
{{ $t("alsijil.coursebook.notices.no_entry") }}
</v-alert>
<v-text-field
v-if="documentation.canEdit"
:class="{
'flex-grow-1 min-width': true,
'full-width': $vuetify.breakpoint.mobile,
}"
hide-details
outlined
:label="$t('alsijil.coursebook.summary.topic.label')"
:value="documentation.topic"
@input="topic = $event"
@focusout="save"
@keydown.enter="saveAndBlur"
:loading="loading"
>
<template #append>
<v-tooltip bottom>
<template v-slot:activator="{ on, attrs }">
<v-scroll-x-transition>
<v-icon
v-if="appendIcon"
:color="appendIconColor"
v-on="on"
v-bind="attrs"
>{{ appendIcon }}</v-icon
>
</v-scroll-x-transition>
</template>
<span>{{ appendIconTooltip }}</span>
</v-tooltip>
</template>
</v-text-field>
<div
:class="{
'flex-grow-1 max-width': true,
'full-width': $vuetify.breakpoint.mobile,
}"
v-if="documentation.canEdit"
>
<v-card
v-bind="dialogActivator.attrs"
v-on="dialogActivator.on"
outlined
@click="$emit('open')"
class="max-width grid-layout pa-1"
dense
rounded="lg"
>
<span class="max-width text-truncate">{{
documentation.homework
? $t("alsijil.coursebook.summary.homework.value", documentation)
: $t("alsijil.coursebook.summary.homework.empty")
}}</span>
<v-icon right class="float-right">{{ homeworkIcon }}</v-icon>
<span class="max-width text-truncate">{{
documentation.groupNote
? $t("alsijil.coursebook.summary.group_note.value", documentation)
: $t("alsijil.coursebook.summary.group_note.empty")
}}</span>
<v-icon right class="float-right">{{ groupNoteIcon }}</v-icon>
</v-card>
</div>
</div>
<!-- not compact -->
<!-- Are focusout & enter enough trigger? -->
<v-text-field
filled
v-if="!compact && documentation.canEdit"
:label="$t('alsijil.coursebook.summary.topic.label')"
:value="documentation.topic"
@input="topic = $event"
/>
<v-textarea
filled
auto-grow
rows="3"
clearable
v-if="!compact && documentation.canEdit"
:label="$t('alsijil.coursebook.summary.homework.label')"
:value="documentation.homework"
@input="homework = $event"
/>
<v-textarea
filled
auto-grow
rows="3"
clearable
v-if="!compact && documentation.canEdit"
:label="$t('alsijil.coursebook.summary.group_note.label')"
:value="documentation.groupNote"
@input="groupNote = $event"
/>
<documentation-full-details
v-if="!compact && !documentation.canEdit"
:documentation="documentation"
/>
</div>
</template>
<script setup>
import DocumentationCompactDetails from "./DocumentationCompactDetails.vue";
import DocumentationFullDetails from "./DocumentationFullDetails.vue";
</script>
<script>
import createOrPatchMixin from "aleksis.core/mixins/createOrPatchMixin.js";
import documentationPartMixin from "./documentationPartMixin";
export default {
name: "LessonSummary",
mixins: [createOrPatchMixin, documentationPartMixin],
emits: ["open"],
data() {
return {
topic: null,
homework: null,
groupNote: null,
appendIcon: null,
topicError: null,
};
},
methods: {
handleUpdateAfterCreateOrPatch(itemId, wasCreate) {
return (cached, incoming) => {
for (const object of incoming) {
console.log("summary: handleUpdateAfterCreateOrPatch", object);
// Replace the current documentation
const index = cached.findIndex(
(o) => o[itemId] === this.documentation.id,
);
// merged with the incoming partial documentation
// if creation of proper documentation from dummy one, set ID of documentation currently being edited as oldID so that key in coursebook doesn't change
cached[index] = {
...this.documentation,
...object,
oldId:
this.documentation.id !== object.id
? this.documentation.id
: this.documentation.oldId,
};
}
return cached;
};
},
handleAppendIconSuccess() {
this.topicError = null;
this.appendIcon = "$success";
setTimeout(() => {
this.appendIcon = "";
}, 3000);
},
save() {
if (
this.topic !== null ||
this.homework !== null ||
this.groupNote !== null
) {
const topic = this.topic !== null ? { topic: this.topic } : {};
const homework =
this.homework !== null ? { homework: this.homework } : {};
const groupNote =
this.groupNote !== null ? { groupNote: this.groupNote } : {};
this.createOrPatch([
{
id: this.documentation.id,
...topic,
...homework,
...groupNote,
},
]);
this.topic = null;
this.homework = null;
this.groupNote = null;
}
},
saveAndBlur(event) {
this.save();
event.target.blur();
},
handleError(error) {
this.appendIcon = "$error";
this.topicError = error;
},
},
computed: {
homeworkIcon() {
if (this.documentation.homework) {
return this.documentation.canEdit
? "mdi-book-edit-outline"
: "mdi-book-alert-outline";
}
return this.documentation.canEdit
? "mdi-book-plus-outline"
: "mdi-book-off-outline";
},
groupNoteIcon() {
if (this.documentation.groupNote) {
return this.documentation.canEdit
? "mdi-note-edit-outline"
: "mdi-note-alert-outline";
}
return this.documentation.canEdit
? "mdi-note-plus-outline"
: "mdi-note-off-outline";
},
minWidth() {
return Math.min(this.documentation?.topic?.length || 15, 15) + "ch";
},
maxWidth() {
return this.$vuetify.breakpoint.mobile ? "100%" : "20ch";
},
appendIconColor() {
return (
{ $success: "success", $error: "error" }[this.appendIcon] || "primary"
);
},
appendIconTooltip() {
return (
{
$success: this.$t("alsijil.coursebook.summary.topic.status.success"),
$error: this.$t("alsijil.coursebook.summary.topic.status.error", {
error: this.topicError,
}),
}[this.appendIcon] || ""
);
},
},
mounted() {
this.$on("save", this.handleAppendIconSuccess);
},
};
</script>
<style scoped>
.min-width {
min-width: v-bind(minWidth);
}
.max-width {
max-width: v-bind(maxWidth);
}
.gap {
gap: 1em;
}
.grid-layout {
display: grid;
grid-template-columns: auto min-content;
}
.read-only-grid {
display: grid;
grid-template-columns: min-content auto;
grid-template-rows: auto;
}
</style>