1
1
<template >
2
2
<NewModal ref =" modal" header =" Creating backup" @show =" focusInput" >
3
3
<div class =" flex flex-col gap-2 md:w-[600px]" >
4
- <div class =" font-semibold text-contrast" >Name</div >
4
+ <label for =" backup-name-input" >
5
+ <span class =" text-lg font-semibold text-contrast" > Name </span >
6
+ </label >
5
7
<input
8
+ id =" backup-name-input"
6
9
ref =" input"
7
10
v-model =" backupName"
8
11
type =" text"
9
12
class =" bg-bg-input w-full rounded-lg p-4"
10
- placeholder =" e.g. Before 1.21 "
11
- maxlength =" 64 "
13
+ : placeholder =" `Backup #${newBackupAmount}` "
14
+ maxlength =" 48 "
12
15
/>
13
- <div class =" flex items-center gap-2 " >
14
- <InfoIcon class =" hidden sm:block" />
15
- <span class =" text-sm text-secondary " >
16
- If left empty, the backup name will default to
17
- < span class = " font-semibold " > Backup #{{ newBackupAmount }}</ span >
16
+ <div v-if = " nameExists && !isCreating " class =" flex items-center gap-1 " >
17
+ <IssuesIcon class =" hidden text-orange sm:block" />
18
+ <span class =" text-sm text-orange " >
19
+ You already have a backup named '< span class = " font-semibold " >{{ trimmedName }}</ span
20
+ >'
18
21
</span >
19
22
</div >
20
23
<div v-if =" isRateLimited" class =" mt-2 text-sm text-red" >
21
24
You're creating backups too fast. Please wait a moment before trying again.
22
25
</div >
23
26
</div >
24
- <div class =" mb-1 mt-4 flex justify-start gap-4 " >
27
+ <div class =" mt-2 flex justify-start gap-2 " >
25
28
<ButtonStyled color =" brand" >
26
- <button :disabled =" isCreating" @click =" createBackup" >
29
+ <button :disabled =" isCreating || nameExists " @click =" createBackup" >
27
30
<PlusIcon />
28
31
Create backup
29
32
</button >
41
44
<script setup lang="ts">
42
45
import { ref , nextTick , computed } from " vue" ;
43
46
import { ButtonStyled , NewModal } from " @modrinth/ui" ;
44
- import { PlusIcon , XIcon , InfoIcon } from " @modrinth/assets" ;
47
+ import { IssuesIcon , PlusIcon , XIcon } from " @modrinth/assets" ;
45
48
46
49
const props = defineProps <{
47
50
server: Server <[" general" , " content" , " backups" , " network" , " startup" , " ws" , " fs" ]>;
48
51
}>();
49
52
50
- const emit = defineEmits ([" backupCreated" ]);
51
-
52
53
const modal = ref <InstanceType <typeof NewModal >>();
53
54
const input = ref <HTMLInputElement >();
54
55
const isCreating = ref (false );
55
56
const isRateLimited = ref (false );
56
- const backupError = ref <string | null >(null );
57
57
const backupName = ref (" " );
58
58
const newBackupAmount = computed (() =>
59
59
props .server .backups ?.data ?.length === undefined ? 1 : props .server .backups ?.data ?.length + 1 ,
60
60
);
61
61
62
+ const trimmedName = computed (() => backupName .value .trim ());
63
+
64
+ const nameExists = computed (() => {
65
+ if (! props .server .backups ?.data ) return false ;
66
+ return props .server .backups .data .some (
67
+ (backup ) => backup .name .trim ().toLowerCase () === trimmedName .value .toLowerCase (),
68
+ );
69
+ });
70
+
62
71
const focusInput = () => {
63
72
nextTick (() => {
64
73
setTimeout (() => {
@@ -67,38 +76,46 @@ const focusInput = () => {
67
76
});
68
77
};
69
78
79
+ function show() {
80
+ backupName .value = " " ;
81
+ isCreating .value = false ;
82
+ modal .value ?.show ();
83
+ }
84
+
70
85
const hideModal = () => {
71
86
modal .value ?.hide ();
72
- backupName .value = " " ;
73
87
};
74
88
75
89
const createBackup = async () => {
76
- if (! backupName .value .trim ()) {
90
+ if (backupName .value .trim (). length === 0 ) {
77
91
backupName .value = ` Backup #${newBackupAmount .value } ` ;
78
92
}
79
93
80
94
isCreating .value = true ;
81
95
isRateLimited .value = false ;
82
96
try {
83
- await props .server .backups ?.create (backupName .value );
84
- await props .server .refresh ();
97
+ await props .server .backups ?.create (trimmedName .value );
85
98
hideModal ();
86
- emit ( " backupCreated " , { success: true , message: " Backup created successfully " } );
99
+ await props . server . refresh ( );
87
100
} catch (error ) {
88
101
if (error instanceof PyroFetchError && error .statusCode === 429 ) {
89
102
isRateLimited .value = true ;
90
- backupError .value = " You're creating backups too fast." ;
103
+ addNotification ({
104
+ type: " error" ,
105
+ title: " Error creating backup" ,
106
+ text: " You're creating backups too fast." ,
107
+ });
91
108
} else {
92
- backupError . value = error instanceof Error ? error .message : String (error );
93
- emit ( " backupCreated " , { success: false , message: backupError . value });
109
+ const message = error instanceof Error ? error .message : String (error );
110
+ addNotification ({ type: " error " , title: " Error creating backup " , text: message });
94
111
}
95
112
} finally {
96
113
isCreating .value = false ;
97
114
}
98
115
};
99
116
100
117
defineExpose ({
101
- show : () => modal . value ?. show () ,
118
+ show ,
102
119
hide: hideModal ,
103
120
});
104
121
</script >
0 commit comments