2024-08-25 00:51:16 +00:00
< template >
2024-09-01 00:54:03 +00:00
< div class = "editor" >
< div class = "editor-header" >
2024-09-01 22:18:39 +00:00
< select
class = "template-select input"
@ change = "selectTemplate(Number($event.target.value), $event)"
>
< option
2024-08-26 04:15:22 +00:00
v - for = "storedTemplate in templates"
: key = "`template-${storedTemplate.id}`"
2024-09-01 22:18:39 +00:00
: value = "storedTemplate.id"
: selected = "selectedTemplate === storedTemplate.id"
> { { storedTemplate . name } } < / option >
< option
v - if = "selectedTemplate === null"
: selected = "selectedTemplate === null"
disabled
> Unsaved < / option >
< / select >
2024-08-26 04:15:22 +00:00
2024-09-01 00:54:03 +00:00
< div
class = "template-add"
@ click = "add"
>
< button class = "button" >
< Icon icon = "file-plus2" / >
< span class = "button-label" > New template < / span >
< / button >
< / div >
< / div >
2024-08-25 00:51:16 +00:00
2024-09-01 00:54:03 +00:00
< textarea
ref = "input"
v - model = "template"
height = "3"
class = "input edit"
@ input = "update"
2024-09-01 22:18:39 +00:00
@ blur = "save(false)"
2024-09-01 00:54:03 +00:00
/ >
< textarea
: value = "summary"
class = "input summary"
: class = "{ error: hasError }"
wrap = "soft"
@ click = "$event.target.select()"
/ >
< div class = "dialog-actions" >
< div class = "actions" >
< button
class = "button"
@ click = "copy"
> Copy < / button >
< button
class = "button"
@ click = "reset"
> Default < / button >
< / div >
2024-08-26 04:15:22 +00:00
2024-09-01 00:54:03 +00:00
< form
class = "actions save"
2024-09-01 22:18:39 +00:00
@ submit . prevent = "save(true)"
2024-09-01 00:54:03 +00:00
>
< Icon
v - if = "selectedTemplate"
icon = "bin"
class = "remove"
@ click = "remove"
/ >
< div class = "actions-save" >
2024-08-25 00:51:16 +00:00
< input
2024-08-26 04:15:22 +00:00
v - model = "templateName"
2024-08-25 00:51:16 +00:00
class = "input"
placeholder = "Name"
2024-08-26 04:15:22 +00:00
required
2024-08-25 00:51:16 +00:00
>
< button
class = "button"
> Save < / button >
2024-09-01 00:54:03 +00:00
< / div >
< / form >
2024-08-25 00:51:16 +00:00
< / div >
2024-09-01 00:54:03 +00:00
< / div >
2024-08-25 00:51:16 +00:00
< / template >
< script setup >
2024-09-01 22:18:39 +00:00
import { ref , inject , onMounted } from 'vue' ;
2024-08-25 00:51:16 +00:00
import { parse } from 'yaml' ;
import Cookies from 'js-cookie' ;
2024-08-26 04:15:22 +00:00
// import slugify from '#/utils/slugify.js';
2024-08-25 00:51:16 +00:00
import events from '#/src/events.js' ;
2024-08-26 04:15:22 +00:00
import { get , post , del } from '#/src/api.js' ;
2024-08-25 00:51:16 +00:00
import processSummaryTemplate from '#/utils/process-summary-template.js' ;
import defaultTemplate from '#/assets/summary.yaml?raw' ; // eslint-disable-line import/no-unresolved
2024-08-26 04:15:22 +00:00
const pageContext = inject ( 'pageContext' ) ;
2024-08-25 00:51:16 +00:00
const cookies = Cookies . withConverter ( {
write : ( value ) => value ,
} ) ;
const props = defineProps ( {
release : {
type : Object ,
default : null ,
} ,
} ) ;
2024-08-26 04:15:22 +00:00
const templates = ref ( pageContext . assets . templates ) ;
2024-09-01 00:54:03 +00:00
const selectedTemplate = ref ( Number ( pageContext . urlParsed . search . t ) || templates . value . at ( 0 ) ? . id || null ) ;
2024-08-26 04:15:22 +00:00
const initialTemplate = templates . value . find ( ( storedTemplate ) => storedTemplate . id === selectedTemplate . value ) || null ;
const template = ref ( initialTemplate ? . template || defaultTemplate ) ;
2024-08-25 00:51:16 +00:00
const hasError = ref ( false ) ;
2024-08-26 04:15:22 +00:00
const input = ref ( null ) ;
2024-09-01 22:18:39 +00:00
const changed = ref ( false ) ;
2024-08-26 04:15:22 +00:00
const templateName = ref ( initialTemplate ? . name || ` custom_ ${ Date . now ( ) } ` ) ;
2024-08-25 00:51:16 +00:00
function getSummary ( ) {
2024-08-26 04:15:22 +00:00
return processSummaryTemplate ( template . value , props . release ) ;
2024-08-25 00:51:16 +00:00
}
const summary = ref ( getSummary ( ) ) ;
2024-08-26 04:15:22 +00:00
function selectTemplate ( templateId ) {
selectedTemplate . value = templateId ;
const nextTemplate = templates . value . find ( ( storedTemplate ) => storedTemplate . id === templateId ) ;
template . value = nextTemplate . template ;
templateName . value = nextTemplate . name ;
summary . value = getSummary ( ) ;
cookies . set ( 'selectedTemplate' , String ( templateId ) ) ;
2024-09-01 22:18:39 +00:00
window . location . href = ` ${ window . location . origin } ${ window . location . pathname } ?t= ${ nextTemplate . id } ` ;
2024-08-26 04:15:22 +00:00
}
2024-08-25 00:51:16 +00:00
function update ( ) {
hasError . value = false ;
2024-09-01 22:18:39 +00:00
changed . value = true ;
2024-08-25 00:51:16 +00:00
try {
summary . value = getSummary ( ) ;
} catch ( error ) {
hasError . value = true ;
}
}
2024-09-01 22:18:39 +00:00
async function save ( explicit ) {
if ( ! explicit && ! changed . value ) {
return ;
}
2024-08-25 00:51:16 +00:00
try {
parse ( template . value ) ;
2024-08-26 04:15:22 +00:00
const createdTemplate = await post ( '/templates' , {
name : templateName . value ,
template : template . value ,
2024-09-01 22:18:39 +00:00
} , {
2024-08-26 04:15:22 +00:00
successFeedback : ` Saved summary template ' ${ templateName . value } ' ` ,
errorFeedback : ` Failed to save summary template ' ${ templateName . value } ' ` ,
2024-08-25 00:51:16 +00:00
} ) ;
2024-08-26 04:15:22 +00:00
2024-09-01 22:18:39 +00:00
changed . value = false ;
2024-08-26 04:15:22 +00:00
templates . value = await get ( ` /users/ ${ pageContext . user . id } /templates ` ) ;
selectTemplate ( createdTemplate . id ) ;
2024-08-25 00:51:16 +00:00
} catch ( error ) {
events . emit ( 'feedback' , {
type : 'error' ,
2024-08-26 04:15:22 +00:00
message : ` Failed to save summary template ' ${ templateName . value } ': ${ error . message } ` ,
} ) ;
}
}
function add ( ) {
selectedTemplate . value = null ;
template . value = '' ;
templateName . value = ` custom_ ${ Date . now ( ) } ` ;
summary . value = '' ;
input . value . focus ( ) ;
}
async function remove ( ) {
if ( confirm ( ` Are you sure you want to delete summary template ${ templateName . value } ? ` ) ) { // eslint-disable-line no-restricted-globals, no-alert
await del ( ` /templates/ ${ selectedTemplate . value } ` , {
undoFeedback : ` Deleted summary template ' ${ templateName . value } ' ` ,
errorFeedback : ` Failed to remove summary template ' ${ templateName . value } ' ` ,
2024-08-25 00:51:16 +00:00
} ) ;
2024-08-26 04:15:22 +00:00
templates . value = await get ( ` /users/ ${ pageContext . user . id } /templates ` ) ;
selectTemplate ( templates . value . at ( - 1 ) ? . id ) ;
2024-08-25 00:51:16 +00:00
}
}
function copy ( ) {
navigator . clipboard . writeText ( summary . value ) ;
events . emit ( 'feedback' , {
type : 'success' ,
message : 'Summary copied to clipboard' ,
} ) ;
}
function reset ( ) {
2024-08-26 04:15:22 +00:00
if ( confirm ( 'Are you sure you want to reset the summary template to the default? Your custom template will be discarded.' ) ) { // eslint-disable-line no-restricted-globals, no-alert
2024-08-25 00:51:16 +00:00
template . value = defaultTemplate ;
update ( ) ;
events . emit ( 'feedback' , {
type : 'undo' ,
message : 'Reset summary template' ,
} ) ;
}
}
2024-09-01 22:18:39 +00:00
onMounted ( ( ) => {
window . addEventListener ( 'beforeunload' , ( event ) => {
if ( changed . value ) {
event . preventDefault ( ) ;
}
} ) ;
} ) ;
2024-08-25 00:51:16 +00:00
< / script >
< style scoped >
2024-09-01 00:54:03 +00:00
. editor {
2024-08-25 00:51:16 +00:00
display : flex ;
2024-09-01 00:54:03 +00:00
flex - grow : 1 ;
flex - direction : column ;
2024-08-25 00:51:16 +00:00
max - width : 100 % ;
}
. input {
resize : none ;
2024-09-01 00:54:03 +00:00
background : var ( -- background ) ;
2024-08-25 00:51:16 +00:00
}
. edit {
flex - grow : 1 ;
2024-09-01 00:54:03 +00:00
min - height : 10 rem ;
resize : vertical ;
2024-08-25 00:51:16 +00:00
}
. summary {
min - height : 4 rem ;
flex - shrink : 0 ;
line - height : 1.5 ;
& . error {
background : var ( -- background - error ) ;
}
}
. dialog - actions {
display : flex ;
justify - content : space - between ; ;
gap : 1 rem ;
2024-09-01 22:18:39 +00:00
padding : 1 rem 0 ;
2024-08-25 00:51:16 +00:00
. input {
flex - grow : 1 ;
width : 0 ;
}
}
. actions {
display : flex ;
2024-08-26 04:15:22 +00:00
2024-09-01 22:18:39 +00:00
. button {
align - items : center ;
}
2024-08-26 04:15:22 +00:00
. button : not ( : last - child ) {
margin - right : 1 rem ;
}
2024-08-25 00:51:16 +00:00
& . save {
flex - grow : 1 ;
justify - content : flex - end ;
2024-08-26 04:15:22 +00:00
. input {
margin - right : 1 rem ;
}
}
. icon {
height : 100 % ;
padding : 0 1 rem ;
cursor : pointer ;
fill : var ( -- glass ) ;
& . remove : hover {
fill : var ( -- error ) ;
}
}
}
2024-09-01 00:54:03 +00:00
. actions - save {
display : flex ;
width : 15 rem ;
}
. editor - header {
display : flex ;
align - items : center ;
justify - content : space - between ;
padding : .5 rem 0 ;
}
2024-08-26 04:15:22 +00:00
. templates {
display : flex ;
align - items : center ;
overflow - x : auto ;
}
. template - key {
2024-09-01 00:54:03 +00:00
padding : .5 rem .75 rem ;
border - radius : .5 rem ;
2024-08-26 04:15:22 +00:00
cursor : pointer ;
2024-09-01 00:54:03 +00:00
color : var ( -- glass - strong - 20 ) ;
font - size : .9 rem ;
font - weight : bold ;
2024-08-26 04:15:22 +00:00
. icon {
fill : var ( -- glass ) ;
}
& . selected {
background : var ( -- primary ) ;
color : var ( -- text - light ) ;
}
& : hover : not ( . selected ) {
color : var ( -- primary ) ;
. icon {
fill : var ( -- primary ) ;
}
2024-08-25 00:51:16 +00:00
}
}
2024-09-01 00:54:03 +00:00
2024-09-01 22:18:39 +00:00
. template - select {
width : 0 ;
flex - grow : 1 ;
max - width : 20 rem ;
}
2024-09-01 00:54:03 +00:00
. template - add {
font - size : 0 ; /* prevent icon jump */
margin - left : .5 rem ;
2024-09-01 22:18:39 +00:00
flex - shrink : 0 ;
}
@ media ( -- compact ) {
. editor {
padding : 0 1 rem ;
}
}
@ media ( -- small - 20 ) {
. editor {
padding : 0 .5 rem ;
}
2024-09-01 00:54:03 +00:00
}
@ media ( -- small - 30 ) {
. dialog - actions {
flex - direction : column ;
}
. actions ,
. actions . save {
justify - content : space - between ;
}
}
2024-08-25 00:51:16 +00:00
< / style >