Newer
Older
hard-panel / panel / frontend / src / pages / SettingsPage.vue
<script setup>
import { computed, onMounted, ref } from 'vue'
import { GnAlert, GnCard, GnSelect, GnSkeleton, GnToolbar } from 'gnexus-ui-kit/vue'
import { useToast } from 'gnexus-ui-kit/vue'

import { api } from '../api.js'
import { locale, setLocale, t } from '../i18n/index.js'

// Первый блок настроек — язык интерфейса. Страница самодостаточна: сама
// запрашивает /me и сама пишет override (PATCH /me при живой сессии либо
// localStorage в открытом режиме).
const toast = useToast()

const me = ref(null) // ответ GET /me целиком
const loadError = ref('')
const loading = ref(true)

// Опции — computed, а не module-level константа: label из t() должен
// перерисовываться при смене языка.
const LANGS = computed(() => [
  { value: 'en', label: t('profile.langEn') },
  { value: 'uk', label: t('profile.langUk') },
  { value: 'ru', label: t('profile.langRu') },
])

const localeOptions = computed(() =>
  me.value?.user
    ? [{ value: '', label: t('profile.languageAuto') }, ...LANGS.value]
    : LANGS.value, // открыта без акаунта: авто нет — есть только явный выбор
)

const currentLocale = computed(() =>
  me.value?.user ? (me.value.locale || '') : locale.value,
)

const description = computed(() =>
  me.value?.user ? t('settings.languageDescription') : t('settings.languageDescriptionOpen'),
)

async function changeLanguage(value) {
  if (!me.value?.user) {
    // открыта без акаунта: выбор живёт в этом браузере (localStorage)
    localStorage.setItem('ghhard.locale', value || 'en')
    setLocale(value)
    toast.success({ title: t('settings.languageSaved') })
    return
  }
  try {
    // '' → 'auto': пустая строка сбрасывает override на бэкенде
    const updated = await api.updateMe({ locale: value || 'auto' })
    me.value = updated
    setLocale(updated.locale_effective)
    toast.success({ title: t('settings.languageSaved') })
  } catch (err) {
    toast.danger({ title: t('common.error'), text: err.message })
  }
}

onMounted(async () => {
  try {
    me.value = await api.me()
    if (me.value.auth_enabled && me.value.user) {
      setLocale(me.value.locale_effective)
    } else {
      me.value = null // режим без аутентификации — пикер с явным выбором
      setLocale(localStorage.getItem('ghhard.locale'))
    }
  } catch (err) {
    if (err?.status !== 401) loadError.value = err.message
    // 401: api.js уже предупредил шлюз входа — страница откроется после входа
  } finally {
    loading.value = false
  }
})
</script>

<template>
  <GnPageHeader :title="t('settings.title')" kicker="GHard Monitor" />

  <GnAlert v-if="loadError" variant="danger" style="margin-bottom: 16px">{{ loadError }}</GnAlert>
  <div v-else-if="loading" style="margin-top: 20px"><GnSkeleton type="card" :count="1" /></div>

  <!-- следующий блок настроек — сюда -->
  <GnCard v-else class="settings-block">
    <GnToolbar :title="t('settings.languageToolbar')" />
    <p class="input-info">{{ description }}</p>
    <div class="create-actions">
      <GnSelect
        class="language-select"
        :model-value="currentLocale"
        :options="localeOptions"
        @update:model-value="changeLanguage"
      />
    </div>
  </GnCard>
</template>

<style scoped>
/* .create-actions нет в CSS кита — creds определяет его сам, сюда тоже */
.settings-block {
  width: 100%;
  max-width: none;
  margin-top: 20px;
}
.create-actions {
  display: flex;
  justify-content: flex-start;
  margin-top: 12px;
}
.create-actions .language-select {
  flex: 0 1 320px;
  min-width: 0;
}
</style>