192 lines
6.0 KiB
Vue
192 lines
6.0 KiB
Vue
<script setup>
|
|
|
|
import { onMounted, ref, useTemplateRef, inject } from 'vue';
|
|
import { Button } from '@cloudron/pankow';
|
|
import { prettyDate } from '@cloudron/pankow/utils';
|
|
import { stripSsoInfo } from '../../utils.js';
|
|
import { marked } from 'marked';
|
|
import AppsModel from '../../models/AppsModel.js';
|
|
|
|
const appsModel = AppsModel.create();
|
|
|
|
const props = defineProps([ 'app' ]);
|
|
const emit = defineEmits([ 'changed' ]);
|
|
|
|
const notesTextarea = useTemplateRef('notesTextarea');
|
|
const showDoneChecklist = ref(false);
|
|
const hasOldChecklist = ref(false);
|
|
const editing = ref(false);
|
|
const busy = ref(false);
|
|
const placeholder = 'Add admin notes here...';
|
|
const noteContent = ref('');
|
|
const profile = inject('profile');
|
|
|
|
async function onAckChecklistItem(item, key) {
|
|
const [error] = await appsModel.ackChecklistItem(props.app.id, key, true);
|
|
if (error) return console.error(error);
|
|
|
|
item.acknowledged = true;
|
|
item.changedAt = Date.now();
|
|
item.changedBy = profile.value.username;
|
|
hasOldChecklist.value = true;
|
|
}
|
|
|
|
async function onSubmit() {
|
|
busy.value = true;
|
|
|
|
// skip saving if unchanged from postInstall
|
|
if (noteContent.value === props.app.manifest.postInstallMessage) {
|
|
busy.value = false;
|
|
editing.value = false;
|
|
return;
|
|
}
|
|
|
|
const [error] = await appsModel.configure(props.app.id, 'notes', { notes: noteContent.value });
|
|
if (error) {
|
|
busy.value = false;
|
|
return console.error(error);
|
|
}
|
|
|
|
// let main view know about this
|
|
emit('changed');
|
|
|
|
editing.value = false;
|
|
busy.value = false;
|
|
}
|
|
|
|
function onEdit() {
|
|
editing.value = true;
|
|
setTimeout(() => notesTextarea.value.focus(), 200);
|
|
}
|
|
|
|
function onDismiss() {
|
|
editing.value = false;
|
|
noteContent.value = props.app.notes === null ? props.app.manifest.postInstallMessage : props.app.notes;
|
|
}
|
|
|
|
onMounted(() => {
|
|
hasOldChecklist.value = !!Object.keys(props.app.checklist).find((k) => { return props.app.checklist[k].acknowledged; });
|
|
noteContent.value = props.app.notes === null ? props.app.manifest.postInstallMessage : props.app.notes;
|
|
|
|
editing.value = false;
|
|
busy.value = false;
|
|
});
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
|
|
<div class="info-row">
|
|
<div class="info-label">{{ $t('app.updates.info.description') }}</div>
|
|
<div class="info-value" v-if="app.appStoreId">{{ app.manifest.title }} {{ app.manifest.version }}</div>
|
|
<div class="info-value" v-else>{{ app.manifest.dockerImage }}</div>
|
|
</div>
|
|
|
|
<div class="info-row">
|
|
<div class="info-label">{{ $t('app.updates.info.appId') }}</div>
|
|
<div class="info-value">{{ app.id }}</div>
|
|
</div>
|
|
|
|
<div class="info-row">
|
|
<div class="info-label">{{ $t('app.updates.info.packageVersion') }}</div>
|
|
<div class="info-value" v-if="app.appStoreId"><a :href="`/#/appstore/${app.manifest.id}?version=${app.manifest.version}`">{{ app.manifest.id }}@{{ app.manifest.version }}</a></div>
|
|
<div class="info-value" v-else>{{ app.manifest.version }}</div>
|
|
</div>
|
|
|
|
<div class="info-row">
|
|
<div class="info-label">{{ $t('app.updates.info.installedAt') }}</div>
|
|
<div class="info-value">{{ prettyDate(app.creationTime) }}</div>
|
|
</div>
|
|
|
|
<div class="info-row">
|
|
<div class="info-label">{{ $t('app.updates.info.lastUpdated') }}</div>
|
|
<div class="info-value">{{ prettyDate(app.updateTime) }}</div>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<div style="display: flex; justify-content: end; align-items: center;">
|
|
<div class="actionable" @click="showDoneChecklist = true" v-show="hasOldChecklist && !showDoneChecklist">{{ $t('app.appInfo.checklistShow') }}</div>
|
|
<div class="actionable" @click="showDoneChecklist = false" v-show="showDoneChecklist">{{ $t('app.appInfo.checklistHide') }}</div>
|
|
</div>
|
|
|
|
<div v-for="(item, key) in app.checklist" :key="key">
|
|
<div class="checklist-item" v-if="!item.acknowledged">
|
|
<span v-html="marked.parse(item.message)"></span>
|
|
<Button small plain tool style="margin-left: 10px;" @click="onAckChecklistItem(item, key)">{{ $t('main.dialog.done') }}</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-for="(item, key) in app.checklist" :key="key" v-show="showDoneChecklist">
|
|
<div class="checklist-item checklist-item-acknowledged" v-if="item.acknowledged">
|
|
<span v-html="marked.parse(item.message)"></span>
|
|
<span class="text-muted text-small">{{ item.changedBy }} - {{ prettyDate(item.changedAt) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<br/>
|
|
|
|
<p>
|
|
<label class="control-label">{{ $t('app.info.notes.title') }}</label><i v-show="!editing" class="info-edit-indicator fa fa-pencil-alt" @click="onEdit()"></i>
|
|
</p>
|
|
|
|
<div>
|
|
<div v-show="!editing">
|
|
<div v-if="noteContent" v-html="marked.parse(stripSsoInfo(noteContent, app.sso))"></div>
|
|
<div v-else class="text-muted hand" @click="onEdit()">{{ placeholder }}</div>
|
|
</div>
|
|
<div v-show="editing">
|
|
<textarea ref="notesTextarea" style="white-space: pre-wrap; margin-bottom: 5px; width: 100%" v-model="noteContent" rows="10"></textarea>
|
|
<div style="display: flex; gap: 5px">
|
|
<Button secondary @click="onDismiss()" v-show="!busy">{{ $t('main.dialog.cancel') }}</Button>
|
|
<Button @click="onSubmit()" :disabled="busy" :loading="busy">{{ $t('app.display.saveAction') }}</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
|
|
.info-edit-indicator {
|
|
float: right;
|
|
border-radius: 20px;
|
|
padding: 5px;
|
|
color: var(--pankow-text-color);
|
|
background-color: var(--pankow-input-background-color);
|
|
transition: all 250ms;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.info-edit-indicator:hover {
|
|
color: white;
|
|
background: var(--pankow-color-primary);
|
|
transform: scale(1.2);
|
|
}
|
|
|
|
.checklist-item {
|
|
padding: 8px;
|
|
border: none;
|
|
border-left: 2px solid rgb(255, 76, 76);
|
|
background-color: #ff000014;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.checklist-item-acknowledged {
|
|
border-left: 2px solid var(--pankow-color-success);
|
|
background-color: transparent;
|
|
}
|
|
|
|
</style>
|
|
|
|
<style>
|
|
|
|
.checklist-item > span > p {
|
|
margin: 0;
|
|
}
|
|
|
|
</style> |