+page.svelte (6891B)
1 <script lang="ts"> 2 import { goto } from "$app/navigation"; 3 import { notif, route } from "$lib/utils/app"; 4 import { 5 import_app_state_from_file, 6 validate_import_file, 7 } from "$lib/utils/backup/import"; 8 import { Fade, Glyph } from "@radroots/apps-lib"; 9 import { LogoCircle, SelectMenu } from "@radroots/apps-lib-pwa"; 10 import type { ExportedAppState } from "@radroots/apps-lib-pwa/types/app"; 11 import { ls } from "$lib/utils/i18n"; 12 import { handle_err } from "@radroots/utils"; 13 14 let loading = $state(false); 15 let current_file: File | null = $state(null); 16 let current_file_validated: ExportedAppState | undefined = 17 $state(undefined); 18 19 $effect(() => { 20 if (current_file) { 21 (async () => { 22 try { 23 const validated = await validate_import_file(current_file); 24 if (validated) current_file_validated = validated; 25 else current_file_validated = undefined; 26 } catch { 27 current_file_validated = undefined; 28 } 29 })(); 30 } 31 }); 32 33 const handle_back = async (): Promise<void> => { 34 await route("/setup"); 35 }; 36 37 const on_change_file = (event: Event): void => { 38 const target = event.currentTarget as HTMLInputElement | null; 39 current_file = target?.files?.[0] ?? null; 40 }; 41 42 const submit = async (): Promise<void> => { 43 try { 44 if (!current_file) 45 return void notif.alert( 46 `${$ls(`error.configuration.import.no_file_chosen`)}`, 47 ); 48 loading = true; 49 const import_result = 50 await import_app_state_from_file(current_file); 51 console.log(`import_result `, import_result); 52 if ("err" in import_result) 53 return void (await notif.alert(import_result.err)); 54 else if (import_result.pass === true) 55 return void (await goto(`/?ref=backup_imported`)); 56 await notif.alert( 57 import_result.message || 58 `${$ls(`error.configuration.import.failure`)}`, 59 ); 60 } catch (e) { 61 handle_err(e, `submit`); 62 } finally { 63 loading = false; 64 } 65 }; 66 </script> 67 68 <button 69 class={`z-10 absolute top-8 right-6 flex flex-row justify-center items-center`} 70 > 71 <SelectMenu 72 basis={{ 73 layer: 0, 74 options: [ 75 { 76 entries: [ 77 { 78 value: "", 79 label: `${$ls(`common.choose_options`)}`, 80 disabled: true, 81 }, 82 { 83 value: "back", 84 label: `${$ls(`common.go_back`)}`, 85 }, 86 ], 87 }, 88 ], 89 callback: async ({ value }) => { 90 if (value === "back") return void (await handle_back()); 91 }, 92 }} 93 > 94 <Glyph 95 basis={{ 96 classes: `text-base text-ly0-gl group-active:text-ly0-gl-a`, 97 key: "gear", 98 }} 99 /> 100 </SelectMenu> 101 </button> 102 103 <div class={`flex flex-col h-screen w-full px-4 justify-center items-start`}> 104 <div class={`flex flex-col w-full gap-4 justify-center items-start `}> 105 <LogoCircle /> 106 <div 107 class={`flex flex-col w-full px-1 gap-1 justify-start items-start`} 108 > 109 <p class={`font-sans font-[600] text-lg text-ly0-gl`}> 110 {`${$ls(`common.import_app_state`)}`} 111 </p> 112 <p class={`font-sans font-[400] text-sm text-ly0-gl/80 max-w-xl`}> 113 {`${$ls(`notification.configuration.import_description`)}`} 114 </p> 115 </div> 116 <div class={`flex flex-row w-full justify-center items-center`}> 117 <input 118 type={`file`} 119 accept={`application/json,.json`} 120 class={`w-full py-2 pl-2 bg-ly0-gl/5 text-ly0-gl text-sm border border-ly0-gl/30 rounded-xl`} 121 onchange={on_change_file} 122 /> 123 </div> 124 <div class={`flex flex-row h-8 w-full justify-start items-start`}> 125 {#if current_file} 126 <Fade 127 basis={{ 128 classes: `flex-row w-full gap-1 justify-center items-center`, 129 }} 130 > 131 {#if current_file_validated} 132 <Glyph 133 basis={{ 134 classes: `text-base text-lime-600`, 135 key: "check-circle", 136 weight: "fill", 137 }} 138 /> 139 <p class={`font-sans font-[500] text-sm text-lime-600`}> 140 {`${$ls( 141 `notification.configuration.backup_file_valid`, 142 { backup_version: current_file_validated.backup_version }, 143 )}`} 144 </p> 145 {:else} 146 <Glyph 147 basis={{ 148 classes: `text-base text-red-400`, 149 key: "x-circle", 150 weight: "fill", 151 }} 152 /> 153 <p class={`font-sans font-[500] text-sm text-red-400`}> 154 {`${$ls(`notification.configuration.backup_file_invalid`)}`} 155 </p> 156 {/if} 157 </Fade> 158 {/if} 159 </div> 160 <div class={`flex flex-row h-8 w-full justify-center items-center`}> 161 {#if current_file && current_file_validated} 162 <Fade basis={{ in: { duration: 400 } }}> 163 <button 164 class={`relative flex flex-row h-8 px-5 justify-center items-center bg-ly1 active:bg-ly1-a rounded-lg text-ly1-gl disabled:opacity-40`} 165 disabled={loading} 166 onclick={submit} 167 > 168 {`${$ls(`common.import`)}`} 169 {#if loading} 170 <Glyph 171 basis={{ 172 classes: `absolute -right-6 text-[1px] text-ly0-gl animate-spin-slow`, 173 key: "circle-notch", 174 }} 175 /> 176 {/if} 177 </button> 178 </Fade> 179 {/if} 180 </div> 181 </div> 182 </div>