Files
myprojplanet_vite/src/components/PageEditor/PageEditor.ts
Surya Paolo 574f389200 - creato editor di Pagine (iniziato)
- fix: mancano i "t," su alcuni componenti...
2025-09-02 16:22:13 +02:00

195 lines
5.6 KiB
TypeScript

import {
defineComponent,
ref,
computed,
onMounted,
watch,
onBeforeUnmount,
toRaw,
nextTick,
} from 'vue';
import { useUserStore } from '@store/UserStore';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { tools } from '@tools';
import { useRouter } from 'vue-router';
import { reactive } from 'vue';
import { IMyPage } from 'app/src/model';
import IconPicker from '../IconPicker/IconPicker.vue';
import { useGlobalStore } from 'app/src/store';
import { storeToRefs } from 'pinia';
export default defineComponent({
name: 'PageEditor',
components: { IconPicker },
props: {
modelValue: {
type: Object as () => IMyPage,
required: true,
},
},
emits: ['update:modelValue', 'apply'],
setup(props, { emit }) {
const $q = useQuasar();
const globalStore = useGlobalStore();
const { mypage } = storeToRefs(globalStore);
// DRaft locale
const draft = reactive<IMyPage>({ ...props.modelValue });
// UI helper: path mostrato con "/" iniziale
const ui = reactive({
pathText: toUiPath(draft.path),
});
const saving = ref(false)
const syncingFromProps = ref(false) // <-- FLAG anti-loop
// --- Watch input esterno: ricarica draft e UI path
// --- Watch input esterno: ricarica draft e UI path (NO scritture nello store qui!)
watch(
() => props.modelValue,
async (v) => {
syncingFromProps.value = true;
Object.assign(draft, v || {});
ui.pathText = toUiPath(draft.path);
await nextTick();
syncingFromProps.value = false;
},
{ deep: false }
);
// --- Ogni modifica del draft: aggiorna store.mypage e emetti update:modelValue (solo se modifica nasce da UI)
watch(
draft,
(val) => {
if (syncingFromProps.value) return; // evita ricorsione
upsertIntoStore(val, mypage.value);
emit('update:modelValue', { ...val });
},
{ deep: true }
);
// --- Helpers path
function toUiPath(storePath?: string) {
const p = (storePath || '').trim();
if (!p) return '/';
return p.startsWith('/') ? p : `/${p}`;
}
function toStorePath(uiPath?: string) {
const p = (uiPath || '').trim();
if (!p) return '';
return p.startsWith('/') ? p.slice(1) : p;
}
function normalizeAndApplyPath() {
// normalizza: niente spazi, minuscole, trattini
let p = (ui.pathText || '/').trim();
p = p.replace(/\s+/g, '-');
if (!p.startsWith('/')) p = '/' + p;
ui.pathText = p;
draft.path = toStorePath(p);
}
function pathRule(v: string) {
if (!v) return 'Percorso richiesto';
if (!v.startsWith('/')) return 'Deve iniziare con /';
if (/\s/.test(v)) return 'Nessuno spazio nel path';
return true;
}
// --- Upsert nello store.mypage
function upsertIntoStore(page: IMyPage, arr: IMyPage[]) {
if (!page) return;
// chiave di matching: prima _id, altrimenti path
const keyId = page._id;
const keyPath = page.path || '';
let idx = -1;
if (keyId) {
idx = arr.findIndex((p) => p._id === keyId);
}
if (idx < 0 && keyPath) {
idx = arr.findIndex((p) => (p.path || '') === keyPath);
}
if (idx >= 0) {
// merge preservando reattività
arr[idx] = { ...arr[idx], ...toRaw(page) };
} else {
arr.push({ ...toRaw(page) });
}
}
async function save () {
try {
saving.value = true
normalizeAndApplyPath() // assicura path coerente
const payload: IMyPage = { ...toRaw(draft), path: draft.path || '' }
const saved = await globalStore.savePage(payload)
if (saved && typeof saved === 'object') {
syncingFromProps.value = true
Object.assign(draft, saved)
upsertIntoStore(draft, mypage.value)
await nextTick()
syncingFromProps.value = false
}
emit('apply', { ...draft })
$q.notify({ type: 'positive', message: 'Pagina salvata' })
} catch (err: any) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel salvataggio' })
} finally {
saving.value = false
}
}
// --- Ricarica da sorgente
async function reloadFromStore () {
try {
const absolute = ui.pathText || '/'
const page = await globalStore.loadPage(absolute, '', true)
if (page) {
syncingFromProps.value = true
Object.assign(draft, page)
ui.pathText = toUiPath(draft.path)
upsertIntoStore(draft, mypage.value)
console.log('page', draft)
emit('update:modelValue', { ...draft })
await nextTick()
syncingFromProps.value = false
$q.notify({ type: 'info', message: 'Pagina ricaricata' })
} else {
$q.notify({ type: 'warning', message: 'Pagina non trovata' })
}
} catch (err) {
console.error(err)
$q.notify({ type: 'negative', message: 'Errore nel ricaricare la pagina' })
}
}
function resetDraft() {
console.log('resetDraft')
syncingFromProps.value = true
Object.assign(draft, props.modelValue || {})
ui.pathText = toUiPath(draft.path)
// aggiorna i componenti
emit('update:modelValue', { ...draft })
nextTick(() => { syncingFromProps.value = false })
}
const absolutePath = computed(() => toUiPath(draft.path))
return {
draft,
ui,
saving,
pathRule,
normalizeAndApplyPath,
save,
reloadFromStore,
resetDraft,
absolutePath,
};
},
});