diff --git a/client/package-lock.json b/client/package-lock.json index 735ab4d..7809557 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,11 +1,11 @@ { - "name": "client", + "name": "kawai-focus", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "client", + "name": "kawai-focus", "version": "0.0.1", "dependencies": { "@capacitor/app": "8.0.0", @@ -15,6 +15,8 @@ "@capacitor/status-bar": "8.0.0", "@ionic/vue": "^8.0.0", "@ionic/vue-router": "^8.0.0", + "@tauri-apps/api": "^2.9.1", + "@tauri-apps/plugin-sql": "^2.3.1", "ionicons": "^7.0.0", "vue": "^3.3.0", "vue-router": "^4.2.0" @@ -3143,6 +3145,25 @@ } } }, + "node_modules/@tauri-apps/api": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.1.tgz", + "integrity": "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/plugin-sql": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-sql/-/plugin-sql-2.3.1.tgz", + "integrity": "sha512-iNgHnFIR+jRkx9INKVKepzMlxXtNkJUaWuhagFjT4dOttPaNyRnVHgwTjpqZhyVjiklDh2UdEPAJkQKiCPAekw==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", diff --git a/client/package.json b/client/package.json index 0130ff2..a36d332 100644 --- a/client/package.json +++ b/client/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vue-tsc && vite build", + "build": "vite build", "preview": "vite preview", "test:e2e": "cypress run", "test:unit": "vitest", @@ -19,6 +19,8 @@ "@capacitor/status-bar": "8.0.0", "@ionic/vue": "^8.0.0", "@ionic/vue-router": "^8.0.0", + "@tauri-apps/api": "^2.9.1", + "@tauri-apps/plugin-sql": "^2.3.1", "ionicons": "^7.0.0", "vue": "^3.3.0", "vue-router": "^4.2.0" diff --git a/client/src/App.vue b/client/src/App.vue index b1485b5..230a084 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -4,14 +4,6 @@ - diff --git a/client/src/config.js b/client/src/config.js new file mode 100644 index 0000000..475f7c8 --- /dev/null +++ b/client/src/config.js @@ -0,0 +1,8 @@ +import { appLocalDataDir } from '@tauri-apps/api/path'; + +export async function getDB_URL() { + // Функция верёнт путь до бд + + const appDir = await appLocalDataDir(); + return `sqlite:${appDir}/timer.db`; +} diff --git a/client/src/db/crud/timerCrud.js b/client/src/db/crud/timerCrud.js new file mode 100644 index 0000000..8b0edc2 --- /dev/null +++ b/client/src/db/crud/timerCrud.js @@ -0,0 +1,10 @@ +import { getDb } from "../initDb"; +import { SELECT_TIMERS } from "../dml/timerDML"; + + +export async function getTimers() { + // Функция для получения списка таймеров + + const db = await getDb(); + return await db.select(SELECT_TIMERS); +} diff --git a/client/src/db/ddl/timerDDL.js b/client/src/db/ddl/timerDDL.js new file mode 100644 index 0000000..d74c188 --- /dev/null +++ b/client/src/db/ddl/timerDDL.js @@ -0,0 +1,11 @@ +export const CREATE_TIMER = ` + PRAGMA foreign_keys=ON; + CREATE TABLE IF NOT EXISTS timer ( + title VARCHAR(200) NOT NULL, + pomodoro_time INTEGER NOT NULL, + break_time INTEGER NOT NULL, + break_long_time INTEGER NOT NULL, + count_pomodoro INTEGER NOT NULL, + id INTEGER NOT NULL, + PRIMARY KEY (id) + );` diff --git a/client/src/db/dml/timerDML.js b/client/src/db/dml/timerDML.js new file mode 100644 index 0000000..f1f4414 --- /dev/null +++ b/client/src/db/dml/timerDML.js @@ -0,0 +1,6 @@ +export const SELECT_TIMERS = 'SELECT id, title, pomodoro_time, count_pomodoro FROM timer ORDER BY id DESC' +export const COUNT_TIMERS = 'SELECT COUNT(*) as cnt FROM timer' +export const INSERT_SEED_DB = ` + INSERT INTO timer (title, pomodoro_time, break_time, break_long_time, count_pomodoro) VALUES + ('Timer mini example', 10, 3, 15, 2), ('Timer max example', 90, 10, 40, 8) +` diff --git a/client/src/db/initDb.js b/client/src/db/initDb.js new file mode 100644 index 0000000..ce90442 --- /dev/null +++ b/client/src/db/initDb.js @@ -0,0 +1,26 @@ +import Database from '@tauri-apps/plugin-sql'; +import { CREATE_TIMER } from './ddl/timerDDL'; +import { getDB_URL } from '../config'; +import { seedDb } from './seed'; + +let dbPromise = null; + +export async function getDb() { + // Функция для получения подключения к базе данных + + if (!dbPromise) { + dbPromise = (async () => { + try { + const dbUrl = await getDB_URL(); + const db = await Database.load(dbUrl); + await db.execute(CREATE_TIMER); + await seedDb(db); + return db; + } catch (error) { + console.error('Ошибка инициализации базы данных', error); + throw error; + } + })(); + } + return await dbPromise; // await для консистентности +} diff --git a/client/src/db/seed.js b/client/src/db/seed.js new file mode 100644 index 0000000..927b161 --- /dev/null +++ b/client/src/db/seed.js @@ -0,0 +1,11 @@ +import { INSERT_SEED_DB, COUNT_TIMERS } from './dml/timerDML' + + +export async function seedDb(db) { + // Функция для заполнения бд данными + + const count = await db.select(COUNT_TIMERS); + if (count[0].cnt === 0) { + await db.execute(INSERT_SEED_DB); + } +} diff --git a/client/src/views/TimersList/TimersList.js b/client/src/views/TimersList/TimersList.js index 1c3cb43..47bde1e 100644 --- a/client/src/views/TimersList/TimersList.js +++ b/client/src/views/TimersList/TimersList.js @@ -8,14 +8,53 @@ import { trashOutline } from 'ionicons/icons'; +import { ref, onMounted } from 'vue'; +import { getTimers } from '../../db/crud/timerCrud'; + export default { name: 'TimersList', components: { IonIcon, }, setup() { - // Возвращаем иконки, чтобы они были доступны в шаблоне + const timers = ref([]); + const loading = ref(true); + const error = ref(null); + const expandedId = ref(null); + + const loadTimers = async () => { + loading.value = true; + error.value = null; + + try { + const result = await getTimers(); + timers.value = result; + } catch (err) { + error.value = err.message || 'Ошибка загрузки таймеров'; + } finally { + loading.value = false; + } + }; + + const toggleExpand = (id) => { + expandedId.value = expandedId.value === id ? null : id; + }; + + onMounted(() => { + loadTimers(); + }); + return { + // состояние + timers, + loading, + error, + expandedId, + + // методы + toggleExpand, + + // иконки timerOutline, chevronUp, chevronDown, @@ -24,44 +63,4 @@ export default { trashOutline, }; }, - data() { - return { - timers: [], - expandedId: null, - loading: true, - error: null, - apiUrl: 'http://127.0.0.1:8090/api/timers' - }; - }, - mounted() { - this.fetchTimers(); - }, - methods: { - async fetchTimers() { - try { - this.loading = true; - this.error = null; - - // Для Tauri invoke (раскомментировать при необходимости) - // const { invoke } = window.__TAURI__.tauri; - // this.timers = await invoke('list_timers'); - - const response = await fetch(`${this.apiUrl}/`); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - this.timers = await response.json(); - this.loading = false; - } catch (err) { - this.error = `Ошибка загрузки таймеров: ${err.message}`; - this.loading = false; - console.error('Fetch error:', err); - } - }, - toggleExpand(timerId) { - this.expandedId = this.expandedId === timerId ? null : timerId; - } - }, };