вирутальная мощность
Gitea Actions Demo / build_and_push (push) Successful in 1m16s
Details
Gitea Actions Demo / build_and_push (push) Successful in 1m16s
Details
This commit is contained in:
parent
75b07057ae
commit
4e34378b05
|
|
@ -2,11 +2,32 @@
|
|||
|
||||
## Task State
|
||||
|
||||
- task_id: TASK-MAP-DOTS-RESET
|
||||
- task_id: TASK-FVP-1..4 (Virtual Power Frontend)
|
||||
- status: success
|
||||
- summary: **Фикс: серые точки на карте нельзя поставить повторно после reset. Корень: `resetChartZoom()` не очищает `mapX` и `clickCoordinates`. Фикс: добавить `mapX.value = []` + `clickCoordinates.value = []` в `resetChartZoom()`; inline onClick вынесен в `onMapClick` (строка 536). `yarn build` exit 0.**
|
||||
- summary: **Все 4 задачи VP-фронтенда завершены: вес в профиле, кнопка+modal+POST, график (merge/separate), avg в шапке. `yarn build` → 0 ошибок. Browser verification pending (requires `yarn up`).**
|
||||
- next task: —
|
||||
- plan: `plans/TASK-MAP-DOTS-RESET.md`
|
||||
- plan: `plans/TASK-FRONTEND-VIRTUAL-POWER.md`
|
||||
|
||||
## ✅ CODER SUCCESS REPORT — TASK-FVP-4
|
||||
|
||||
- `src/pages/workouts/components/WorkoutItem.vue`: добавлена карточка `v-if="workoutItem.virtual_power != null"` после блока «Максимальная мощность» (линия 151), перед блоком «Сделать публичной».
|
||||
- `yarn build` (vue-tsc --noEmit + vite) → exit 0.
|
||||
- Lint: предсуществующая `tsutils.iterateComments` (не связана с изменением).
|
||||
|
||||
## ✅ CODER SUCCESS REPORT — TASK-FVP-2
|
||||
|
||||
- `components/WorkoutItem.vue`: кнопка VP (active state по `virtual_power_bike_type`), VaModal с 2 VaSelect, `calculateVP()` POST, emit `vpCalculated`, error handling (400/422).
|
||||
- `pages/WorkoutItem.vue`: `@vpCalculated="initWorkout(...)"`.
|
||||
- `Definitions.vue`: добавлено `virtual_power_bike_type?: string | null`.
|
||||
- 6 locale-файлов: 12 ключей `virtualPower*`.
|
||||
- `yarn build` → 0 ошибок.
|
||||
|
||||
## ✅ CODER SUCCESS REPORT — TASK-FVP-1
|
||||
|
||||
- `src/stores/user-store.ts`: `weight: null as number | null` в SSR + localStorage hydration, action `setWeight(w)`.
|
||||
- `src/pages/preferences/settings/Settings.vue`: 4-я строка с `VaInput type="number"` + save button, `PATCH /api/v0/profiles/{id}` с `{ profile: { weight } }`, toast success/error.
|
||||
- 6 locale-файлов: `"preferences": { "weight": "..." }`.
|
||||
- `yarn build` → 0 ошибок. Lint: предсуществующая `tsutils.iterateComments` (не связана).
|
||||
|
||||
## ✅ CODER SUCCESS REPORT — TASK-INTEG-SECTION (+TASK-SECTION-BUGS)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
# Progress — Strava Frontend
|
||||
|
||||
## 2026-10-02 — TASK-FVP-4: карточка «Вирт. мощность» в шапке тренировки
|
||||
|
||||
- `src/pages/workouts/components/WorkoutItem.vue`: блок `v-if="workoutItem.virtual_power != null"` (label `t("workout.virtualPower")`, значение `Math.floor(workoutItem.virtual_power)` Вт), вставлен после «Максимальная мощность» (линия 151), перед «Сделать публичной».
|
||||
- Verified: `yarn build` (vue-tsc --noEmit + vite) → exit 0. Lint: только предсуществующая `tsutils.iterateComments` (не связана).
|
||||
|
||||
## 2026-09-26 — TASK-SECTION-REZOOM-LOOP: фикс бесконечного цикла re-render (CPU-шум)
|
||||
|
||||
- `src/pages/workouts/components/WorkoutItem.vue`:
|
||||
|
|
|
|||
|
|
@ -159,6 +159,21 @@
|
|||
"fileDeleted": "Arquivo excluído"
|
||||
},
|
||||
"workout": {
|
||||
"hide_start_end": "Ocultar início e fim da trilha"
|
||||
"hide_start_end": "Ocultar início e fim da trilha",
|
||||
"virtualPower": "Potência virtual",
|
||||
"virtualPowerBtn": "Calcular potência virtual",
|
||||
"virtualPowerBikeType": "Tipo de bicicleta",
|
||||
"virtualPowerRoad": "Estrada",
|
||||
"virtualPowerOther": "Outro",
|
||||
"virtualPowerSurface": "Superfície",
|
||||
"virtualPowerAsphalt": "Asfalto",
|
||||
"virtualPowerGravel": "Cascalho",
|
||||
"virtualPowerDirt": "Terra",
|
||||
"virtualPowerSnow": "Neve",
|
||||
"virtualPowerCalculate": "Calcular",
|
||||
"virtualPowerNeedWeight": "Defina seu peso nas configurações primeiro"
|
||||
},
|
||||
"preferences": {
|
||||
"weight": "Peso (kg)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,21 @@
|
|||
"fileDeleted": "文件已删除"
|
||||
},
|
||||
"workout": {
|
||||
"hide_start_end": "隐藏轨迹起点和终点"
|
||||
"hide_start_end": "隐藏轨迹起点和终点",
|
||||
"virtualPower": "虚拟功率",
|
||||
"virtualPowerBtn": "计算虚拟功率",
|
||||
"virtualPowerBikeType": "自行车类型",
|
||||
"virtualPowerRoad": "公路",
|
||||
"virtualPowerOther": "其他",
|
||||
"virtualPowerSurface": "路面",
|
||||
"virtualPowerAsphalt": "柏油路",
|
||||
"virtualPowerGravel": "碎石",
|
||||
"virtualPowerDirt": "泥土",
|
||||
"virtualPowerSnow": "雪",
|
||||
"virtualPowerCalculate": "计算",
|
||||
"virtualPowerNeedWeight": "请先在个人资料设置中设置体重"
|
||||
},
|
||||
"preferences": {
|
||||
"weight": "体重 (kg)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,21 @@
|
|||
"fileDeleted": "Archivo eliminado"
|
||||
},
|
||||
"workout": {
|
||||
"hide_start_end": "Ocultar inicio y fin de la ruta"
|
||||
"hide_start_end": "Ocultar inicio y fin de la ruta",
|
||||
"virtualPower": "Potencia virtual",
|
||||
"virtualPowerBtn": "Calcular potencia virtual",
|
||||
"virtualPowerBikeType": "Tipo de bicicleta",
|
||||
"virtualPowerRoad": "Carretera",
|
||||
"virtualPowerOther": "Otro",
|
||||
"virtualPowerSurface": "Superficie",
|
||||
"virtualPowerAsphalt": "Asfalto",
|
||||
"virtualPowerGravel": "Grava",
|
||||
"virtualPowerDirt": "Tierra",
|
||||
"virtualPowerSnow": "Nieve",
|
||||
"virtualPowerCalculate": "Calcular",
|
||||
"virtualPowerNeedWeight": "Establezca su peso en la configuración primero"
|
||||
},
|
||||
"preferences": {
|
||||
"weight": "Peso (kg)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,6 +185,21 @@
|
|||
"fileDeleted": "File deleted"
|
||||
},
|
||||
"workout": {
|
||||
"hide_start_end": "Hide start and end of track"
|
||||
"hide_start_end": "Hide start and end of track",
|
||||
"virtualPower": "Virtual Power",
|
||||
"virtualPowerBtn": "Calculate Virtual Power",
|
||||
"virtualPowerBikeType": "Bike type",
|
||||
"virtualPowerRoad": "Road",
|
||||
"virtualPowerOther": "Other",
|
||||
"virtualPowerSurface": "Surface",
|
||||
"virtualPowerAsphalt": "Asphalt",
|
||||
"virtualPowerGravel": "Gravel",
|
||||
"virtualPowerDirt": "Dirt",
|
||||
"virtualPowerSnow": "Snow",
|
||||
"virtualPowerCalculate": "Calculate",
|
||||
"virtualPowerNeedWeight": "Set your weight in profile settings first"
|
||||
},
|
||||
"preferences": {
|
||||
"weight": "Weight (kg)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -200,6 +200,21 @@
|
|||
"fileDeleted": "فایل حذف شد"
|
||||
},
|
||||
"workout": {
|
||||
"hide_start_end": "پنهان کردن شروع و پایان مسیر"
|
||||
"hide_start_end": "پنهان کردن شروع و پایان مسیر",
|
||||
"virtualPower": "توان مجازی",
|
||||
"virtualPowerBtn": "محاسبه توان مجازی",
|
||||
"virtualPowerBikeType": "نوع دوچرخه",
|
||||
"virtualPowerRoad": "جادهای",
|
||||
"virtualPowerOther": "دیگر",
|
||||
"virtualPowerSurface": "سطح",
|
||||
"virtualPowerAsphalt": "آسفالت",
|
||||
"virtualPowerGravel": "شن و ماسه",
|
||||
"virtualPowerDirt": "گل",
|
||||
"virtualPowerSnow": "برف",
|
||||
"virtualPowerCalculate": "محاسبه",
|
||||
"virtualPowerNeedWeight": "ابتدا وزن خود را در تنظیمات پروفایل وارد کنید"
|
||||
},
|
||||
"preferences": {
|
||||
"weight": "وزن (کیلوگرم)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,6 +189,21 @@
|
|||
"fileDeleted": "Файл удалён"
|
||||
},
|
||||
"workout": {
|
||||
"hide_start_end": "Скрыть начало и конец трека"
|
||||
"hide_start_end": "Скрыть начало и конец трека",
|
||||
"virtualPower": "Вирт. мощность",
|
||||
"virtualPowerBtn": "Рассчитать вирт. мощность",
|
||||
"virtualPowerBikeType": "Тип велосипеда",
|
||||
"virtualPowerRoad": "Шоссейный",
|
||||
"virtualPowerOther": "Другой",
|
||||
"virtualPowerSurface": "Покрытие",
|
||||
"virtualPowerAsphalt": "Асфальт",
|
||||
"virtualPowerGravel": "Гравий",
|
||||
"virtualPowerDirt": "Грязь",
|
||||
"virtualPowerSnow": "Снег",
|
||||
"virtualPowerCalculate": "Рассчитать",
|
||||
"virtualPowerNeedWeight": "Сначала укажите вес в настройках профиля"
|
||||
},
|
||||
"preferences": {
|
||||
"weight": "Вес (кг)"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,13 +45,68 @@
|
|||
</VaButton>
|
||||
</div>
|
||||
<VaDivider />
|
||||
<div
|
||||
class="flex flex-col md:flex-row md:items-center space-y-2 md:space-y-0 md:space-x-6 min-h-[36px] leading-5"
|
||||
>
|
||||
<p class="font-bold w-[200px]">{{ t("preferences.weight") }}</p>
|
||||
<div class="flex-1 flex items-center gap-2">
|
||||
<VaInput
|
||||
v-model.number="weightValue"
|
||||
type="number"
|
||||
class="max-w-[200px]"
|
||||
:placeholder="String(store.weight ?? 75)"
|
||||
/>
|
||||
<span class="text-sm text-gray-500">кг</span>
|
||||
<VaButton
|
||||
:style="buttonStyles"
|
||||
preset="primary"
|
||||
size="small"
|
||||
class="w-fit h-fit"
|
||||
:loading="savingWeight"
|
||||
@click="saveWeight"
|
||||
>
|
||||
Сохранить
|
||||
</VaButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, inject } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useToast } from "vuestic-ui/web-components";
|
||||
import { AxiosInstance } from "axios";
|
||||
import { useUserStore } from "../../../stores/user-store";
|
||||
|
||||
import { buttonStyles } from "../styles";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { init } = useToast();
|
||||
const store = useUserStore();
|
||||
const axiosAuth = inject("axiosAuth") as AxiosInstance;
|
||||
|
||||
const weightValue = ref<number | undefined>(store.weight ?? undefined);
|
||||
const savingWeight = ref(false);
|
||||
|
||||
const emits = defineEmits(["openNameModal", "openResetPasswordModal"]);
|
||||
|
||||
const saveWeight = () => {
|
||||
if (weightValue.value === undefined || weightValue.value === null) return;
|
||||
savingWeight.value = true;
|
||||
const dataProfile = JSON.parse(localStorage.getItem("profile") || "{}");
|
||||
dataProfile.weight = weightValue.value;
|
||||
localStorage.setItem("profile", JSON.stringify(dataProfile));
|
||||
axiosAuth
|
||||
.patch(`/api/v0/profiles/${store.profileID}`, {
|
||||
profile: { weight: weightValue.value },
|
||||
})
|
||||
.then(() => {
|
||||
store.setWeight(weightValue.value as number);
|
||||
init({ message: "Вес сохранён", color: "success" });
|
||||
})
|
||||
.catch(() => {
|
||||
init({ message: "Ошибка сохранения", color: "error" });
|
||||
})
|
||||
.finally(() => {
|
||||
savingWeight.value = false;
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ export type WorkoutItem = {
|
|||
is_public: boolean;
|
||||
hide_start_end: boolean;
|
||||
workouted_at: string;
|
||||
virtual_power_bike_type?: string | null;
|
||||
virtual_power?: number | null;
|
||||
virtual_power_surface?: string | null;
|
||||
external_links?: WorkoutLink;
|
||||
photos?: Array<WorkoutPhoto>;
|
||||
};
|
||||
|
|
@ -78,6 +81,7 @@ export type ChartDataset = {
|
|||
borderColor: string;
|
||||
data: Array<number | null>;
|
||||
yAxisID?: string;
|
||||
borderDash?: Array<number>;
|
||||
};
|
||||
export type ChartData = {
|
||||
labels: Array<string | number>;
|
||||
|
|
@ -87,6 +91,7 @@ export type ChartDataByMetric = {
|
|||
speed?: ChartData;
|
||||
heartRate?: ChartData;
|
||||
power?: ChartData;
|
||||
virtualPower?: ChartData;
|
||||
elevation?: ChartData;
|
||||
};
|
||||
export default {};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
:distances="distances"
|
||||
:dzenLink="dzenLink"
|
||||
:isPrivate="true"
|
||||
@vpCalculated="initWorkout(route.params.id as string)"
|
||||
/>
|
||||
<div v-else>
|
||||
<VaInnerLoading loading :size="60"> </VaInnerLoading>
|
||||
|
|
|
|||
|
|
@ -40,10 +40,12 @@ export const GetWorkout = (url: string) => {
|
|||
const speed = [];
|
||||
const heart_rate = [];
|
||||
const power = [];
|
||||
const virtual_power = [];
|
||||
const coords = [];
|
||||
const elevation = [];
|
||||
let is_elevation = false;
|
||||
let is_power = false;
|
||||
let is_virtual_power = false;
|
||||
let is_heart_rate = false;
|
||||
let is_speed = false;
|
||||
for (const i in response.data.results) {
|
||||
|
|
@ -60,6 +62,10 @@ export const GetWorkout = (url: string) => {
|
|||
if (response.data.results[i].power !== null) {
|
||||
is_power = true;
|
||||
}
|
||||
virtual_power.push(response.data.results[i].virtual_power ?? null);
|
||||
if (response.data.results[i].virtual_power != null) {
|
||||
is_virtual_power = true;
|
||||
}
|
||||
heart_rate.push(response.data.results[i].heart_rate);
|
||||
if (response.data.results[i].heart_rate !== null) {
|
||||
is_heart_rate = true;
|
||||
|
|
@ -126,6 +132,36 @@ export const GetWorkout = (url: string) => {
|
|||
],
|
||||
};
|
||||
}
|
||||
if (is_virtual_power) {
|
||||
if (is_power) {
|
||||
// Merge into power chart as second dataset (dashed orange)
|
||||
byMetric.power!.datasets.push({
|
||||
yAxisID: "logAxis",
|
||||
radius: 0,
|
||||
label: "Вирт. мощность",
|
||||
borderColor: "#ff9800",
|
||||
backgroundColor: "#ff9800",
|
||||
borderDash: [5, 5],
|
||||
data: virtual_power,
|
||||
});
|
||||
} else {
|
||||
// Separate chart when no real power
|
||||
byMetric.virtualPower = {
|
||||
labels: times,
|
||||
datasets: [
|
||||
{
|
||||
yAxisID: "logAxis",
|
||||
radius: 0,
|
||||
label: "Вирт. мощность",
|
||||
borderColor: "#ff9800",
|
||||
backgroundColor: "#ff9800",
|
||||
borderDash: [5, 5],
|
||||
data: virtual_power,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
if (is_elevation) {
|
||||
byMetric.elevation = {
|
||||
labels: times,
|
||||
|
|
|
|||
|
|
@ -149,6 +149,14 @@
|
|||
{{ Math.floor(workoutItem.max_power) }} Вт
|
||||
</div>
|
||||
</div>
|
||||
<div class="workout-item-params" v-if="workoutItem.virtual_power != null">
|
||||
<div class="workout-item-params-name">
|
||||
{{ t("workout.virtualPower") }}:
|
||||
</div>
|
||||
<div class="workout-item-params-value">
|
||||
{{ Math.floor(workoutItem.virtual_power) }} Вт
|
||||
</div>
|
||||
</div>
|
||||
<div class="workout-item-params" v-if="isPrivate">
|
||||
<div class="workout-item-params-name">Сделать публичной: </div>
|
||||
<input
|
||||
|
|
@ -167,6 +175,21 @@
|
|||
v-on:change="changeHideStartEnd(workoutItem.hide_start_end)"
|
||||
/>
|
||||
</div>
|
||||
<div class="workout-item-params" v-if="isPrivate">
|
||||
<div class="workout-item-params-name">
|
||||
{{ t("workout.virtualPowerBtn") }}:
|
||||
</div>
|
||||
<VaButton
|
||||
preset="secondary"
|
||||
size="small"
|
||||
:color="
|
||||
workoutItem!.virtual_power_bike_type ? 'primary' : 'textPrimary'
|
||||
"
|
||||
@click="showVPModal = true"
|
||||
>
|
||||
<VaIcon name="flash_on" /> {{ t("workout.virtualPower") }}
|
||||
</VaButton>
|
||||
</div>
|
||||
<div class="workout-item-params" v-if="dzenLink || isPrivate">
|
||||
<div class="workout-item-params-name">Ссылки на описание: </div>
|
||||
<div v-if="dzenLink">
|
||||
|
|
@ -366,6 +389,31 @@
|
|||
/>
|
||||
</VaForm>
|
||||
</VaModal>
|
||||
<VaModal
|
||||
v-model="showVPModal"
|
||||
size="small"
|
||||
hide-default-actions
|
||||
close-button
|
||||
@update:modelValue="(v: boolean) => (showVPModal = v)"
|
||||
>
|
||||
<h1 class="va-h5 mb-4">{{ t("workout.virtualPowerBtn") }}</h1>
|
||||
<VaForm @submit.prevent="calculateVP">
|
||||
<p class="mb-1 font-bold">{{ t("workout.virtualPowerBikeType") }}</p>
|
||||
<VaSelect v-model="vpBikeType" :items="bikeTypeOptions" class="mb-4" />
|
||||
<p class="mb-1 font-bold">{{ t("workout.virtualPowerSurface") }}</p>
|
||||
<VaSelect v-model="vpSurface" :items="surfaceOptions" class="mb-4" />
|
||||
<div class="flex justify-end">
|
||||
<VaButton
|
||||
type="submit"
|
||||
preset="primary"
|
||||
size="small"
|
||||
:loading="vpLoading"
|
||||
>
|
||||
{{ t("workout.virtualPowerCalculate") }}
|
||||
</VaButton>
|
||||
</div>
|
||||
</VaForm>
|
||||
</VaModal>
|
||||
<div
|
||||
v-if="showLightbox && lightboxSet.length > 0"
|
||||
class="photo-lightbox"
|
||||
|
|
@ -1131,6 +1179,13 @@ const chartConfigs = computed<Array<ChartConfig>>(() => {
|
|||
yScaleById.yGroung.options,
|
||||
);
|
||||
push("power", "Мощность", data.power, "logAxis", yScaleById.logAxis.options);
|
||||
push(
|
||||
"virtualPower",
|
||||
"Вирт. мощность",
|
||||
data.virtualPower,
|
||||
"logAxis",
|
||||
yScaleById.logAxis.options,
|
||||
);
|
||||
return configs;
|
||||
});
|
||||
const collectCharts = () => {
|
||||
|
|
@ -1192,6 +1247,61 @@ onMounted(() => {
|
|||
group.charts = collectCharts();
|
||||
});
|
||||
const axiosAuth = inject("axiosAuth") as AxiosInstance;
|
||||
|
||||
const showVPModal = ref(false);
|
||||
const vpBikeType = ref("road");
|
||||
const vpSurface = ref("asphalt");
|
||||
const vpLoading = ref(false);
|
||||
|
||||
const bikeTypeOptions = computed(() => [
|
||||
{ label: t("workout.virtualPowerRoad"), value: "road" },
|
||||
{ label: t("workout.virtualPowerOther"), value: "other" },
|
||||
]);
|
||||
const surfaceOptions = computed(() => [
|
||||
{ label: t("workout.virtualPowerAsphalt"), value: "asphalt" },
|
||||
{ label: t("workout.virtualPowerGravel"), value: "gravel" },
|
||||
{ label: t("workout.virtualPowerDirt"), value: "dirt" },
|
||||
{ label: t("workout.virtualPowerSnow"), value: "snow" },
|
||||
]);
|
||||
|
||||
const emits = defineEmits(["vpCalculated"]);
|
||||
|
||||
const calculateVP = () => {
|
||||
vpLoading.value = true;
|
||||
const id = workoutItem!.id;
|
||||
axiosAuth
|
||||
.post(`/api/v0/workouts/${id}/virtual-power`, {
|
||||
bike_type: vpBikeType.value,
|
||||
surface: vpSurface.value,
|
||||
})
|
||||
.then(() => {
|
||||
showVPModal.value = false;
|
||||
emits("vpCalculated");
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
if (err instanceof AxiosError) {
|
||||
if (err.response?.status === 400) {
|
||||
init({
|
||||
message: t("workout.virtualPowerNeedWeight"),
|
||||
color: "warning",
|
||||
});
|
||||
} else if (err.response?.status === 422) {
|
||||
const detail = err.response.data?.detail;
|
||||
const msg = Array.isArray(detail)
|
||||
? detail.map((d: { msg?: string }) => d.msg || "").join("; ")
|
||||
: "Validation error";
|
||||
init({ message: msg || "Validation error", color: "error" });
|
||||
} else {
|
||||
init({ message: "Что-то пошло не так.", color: "error" });
|
||||
}
|
||||
} else {
|
||||
init({ message: "Что-то пошло не так.", color: "error" });
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
vpLoading.value = false;
|
||||
});
|
||||
};
|
||||
const saveLink = (hide: () => void) => {
|
||||
if (!dzenLink.value) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export const useUserStore = defineStore("user", {
|
|||
memberSince: "",
|
||||
pfp: "",
|
||||
is2FAEnabled: false,
|
||||
weight: null as number | null,
|
||||
};
|
||||
}
|
||||
const defaultAvatar =
|
||||
|
|
@ -25,6 +26,7 @@ export const useUserStore = defineStore("user", {
|
|||
memberSince: "",
|
||||
pfp: "",
|
||||
is2FAEnabled: false,
|
||||
weight: null as number | null,
|
||||
};
|
||||
}
|
||||
const dataProfile = JSON.parse(localStorage.getItem("profile")!);
|
||||
|
|
@ -56,6 +58,9 @@ export const useUserStore = defineStore("user", {
|
|||
memberSince: formattedToday,
|
||||
pfp: "https://picsum.photos/id/22/200/300",
|
||||
is2FAEnabled: false,
|
||||
weight:
|
||||
((dataProfile as Record<string, unknown>).weight as number | null) ??
|
||||
null,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -71,5 +76,8 @@ export const useUserStore = defineStore("user", {
|
|||
changeAvatar(avatar: string) {
|
||||
this.avatar = avatar;
|
||||
},
|
||||
setWeight(w: number | null) {
|
||||
this.weight = w;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue