Bunch of little changes.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Ryan Pandya 2023-05-15 17:53:56 -04:00
parent d2fa100a89
commit 2eea51828a
31 changed files with 5439 additions and 846 deletions

View File

@ -17,29 +17,37 @@ const app = express()
app.use(morgan('combined')) app.use(morgan('combined'))
app.use(bodyParser.json()) app.use(bodyParser.json())
app.use(cors()) app.use(cors())
app.get('/database', (req, res) => {
const promise = databases.listDocuments('lifetracker-db', 'ryan'); // app.get('/database', (req, res) => {
promise.then(function ({ documents } = response) { // const promise = databases.listDocuments('lifetracker-db', 'ryan');
// promise.then(function ({ documents } = response) {
res.send(documents) // res.send(documents)
}, function (error) { // }, function (error) {
console.log(error); // console.log(error);
}); // });
}) // })
app.get('/entry/:date', (req, res) => { // app.get('/entry/:date', (req, res) => {
var date = new Date(req.params['date']).toISOString().replace(/T.*/,""); // var date = new Date(req.params['date']).toISOString().replace(/T.*/,"");
console.log("Fetching entry for " + date); // console.log("Fetching entry for " + date);
const promise = databases.listDocuments('lifetracker-db', 'ryan'); // const promise = databases.listDocuments('lifetracker-db', 'ryan');
promise.then(function ({ documents } = response) { // promise.then(function ({ documents } = response) {
entries = documents.map(e => e.Date) // entries = documents.map(e => e.Date)
res.send(entries) // res.send(entries)
}, function (error) { // }, function (error) {
console.log(error); // console.log(error);
}); // });
}) // })
const path = __dirname + '/dist/'
app.use(express.static(path))
app.get(/.*/, function (req,res) {
res.sendFile(path + "index.html");
});
app.listen(process.env.PORT || 8081) app.listen(process.env.PORT || 8081)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

15
lifetracker-server/src/dist/index.html vendored Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Life Tracker Expanded</title>
<script type="module" crossorigin src="/assets/index-64281767.js"></script>
<link rel="stylesheet" href="/assets/index-b41ae507.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@ -8,8 +8,12 @@ const client = new sdk.Client();
const databases = new sdk.Databases(client); const databases = new sdk.Databases(client);
function importCSV(){
console.log("Worked.");
}
var data = fs.readFileSync('data.csv') function importsCSV(){
var data = fs.readFileSync('data.csv')
.toString() // convert Buffer to string .toString() // convert Buffer to string
.split('\n') // split string to lines .split('\n') // split string to lines
.slice(1) // remove first line .slice(1) // remove first line
@ -41,5 +45,6 @@ var data = fs.readFileSync('data.csv')
console.log(error); console.log(error);
}); });
}) })
}
exports.entries = data; exports.entries = importCSV;

View File

@ -0,0 +1,20 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"standard-with-typescript",
"prettier",
],
overrides: [],
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
project: ["tsconfig.json"],
},
plugins: ["vue"],
rules: {},
};

View File

@ -0,0 +1 @@
{}

View File

@ -13,8 +13,8 @@ TypeScript cannot handle type information for `.vue` imports by default, so we r
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
1. Disable the built-in TypeScript Extension 1. Disable the built-in TypeScript Extension
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette 1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` 2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration ## Customize configuration

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,8 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
"format": "prettier . --write",
"build": "run-p type-check build-only", "build": "run-p type-check build-only",
"preview": "vite preview", "preview": "vite preview",
"build-only": "vite build", "build-only": "vite build",
@ -11,14 +13,18 @@
}, },
"dependencies": { "dependencies": {
"@handsontable/vue3": "^12.3.3", "@handsontable/vue3": "^12.3.3",
"@vitejs/plugin-react-refresh": "^1.3.6",
"appwrite": "^11.0.0", "appwrite": "^11.0.0",
"axios": "^1.4.0", "axios": "^1.4.0",
"eslint-config-standard-with-typescript": "^34.0.1",
"eslint-plugin-vue": "^9.12.0",
"handsontable": "^12.3.3", "handsontable": "^12.3.3",
"luxon": "^3.3.0", "luxon": "^3.3.0",
"moment": "^2.29.4", "moment": "^2.29.4",
"node": "^20.0.0", "node": "^20.0.0",
"pinia": "^2.0.35", "pinia": "^2.0.35",
"semver": "^7.5.1", "semver": "^7.5.1",
"vite-plugin-eslint": "^1.8.1",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-router": "^4.1.6" "vue-router": "^4.1.6"
}, },
@ -26,8 +32,10 @@
"@types/node": "^18.14.2", "@types/node": "^18.14.2",
"@vitejs/plugin-vue": "^4.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue/tsconfig": "^0.1.3", "@vue/tsconfig": "^0.1.3",
"eslint-config-prettier": "^8.8.0",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"typescript": "~4.8.4", "prettier": "2.8.8",
"typescript": "^5.1.0-dev.20230515",
"vite": "^4.1.4", "vite": "^4.1.4",
"vue-tsc": "^1.2.0" "vue-tsc": "^1.2.0"
} }

View File

@ -1,15 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router' import { RouterLink, RouterView } from "vue-router";
import AuthNav from './components/AuthNav.vue' import AuthNav from "./components/AuthNav.vue";
</script> </script>
<template> <template>
<header> <header>
<div class="title"> <div class="title">
<h1> <h1>
<RouterLink to="/"> <RouterLink to="/"> Tracker Expanded </RouterLink>
Tracker Expanded
</RouterLink>
</h1> </h1>
</div> </div>
<nav> <nav>
@ -20,9 +18,6 @@ import AuthNav from './components/AuthNav.vue'
</header> </header>
<RouterView /> <RouterView />
</template> </template>
<style scoped> <style scoped></style>
</style>

View File

@ -63,46 +63,46 @@ body {
background: var(--color-background); background: var(--color-background);
transition: color 0.5s, background-color 0.5s; transition: color 0.5s, background-color 0.5s;
line-height: 1.6; line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
font-size: 15px; font-size: 15px;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
header{ header {
display: flex; display: flex;
gap: 2em; gap: 2em;
align-items: center; align-items: center;
/* border-bottom: 1px solid var(--vt-c-white-mute); */ /* border-bottom: 1px solid var(--vt-c-white-mute); */
} }
h1{ h1 {
color: var(--vt-c-text-dark-1); color: var(--vt-c-text-dark-1);
} }
nav{ nav {
display:flex; display: flex;
gap: 1em; gap: 1em;
} }
a{ a {
color: var(--color-text); color: var(--color-text);
text-decoration: none; text-decoration: none;
} }
a.router-link-active{ a.router-link-active {
font-weight: bold; font-weight: bold;
border-bottom: 1px solid var(--color-text); border-bottom: 1px solid var(--color-text);
} }
a:hover{ a:hover {
color: white; color: white;
} }
div#app{ div#app {
height: 95vh; height: 95vh;
} }
div#table{ div#table {
height: 100%; height: 100%;
} }

View File

@ -1,4 +1,4 @@
:root{ :root {
--white: white; --white: white;
--black: #273036; --black: #273036;
--red: #c71634; --red: #c71634;
@ -13,11 +13,13 @@
--lime: #bfff55; --lime: #bfff55;
} }
.handsontable td.align-left, .handsontable th:last-child div { .handsontable td.align-left,
.handsontable th:last-child div {
text-align: left !important; text-align: left !important;
} }
.ht__active_highlight, .ht__highlight { .ht__active_highlight,
.ht__highlight {
background: unset; background: unset;
} }
@ -28,11 +30,11 @@
.invert { .invert {
filter: invert(); filter: invert();
} }
.active-hour{ .active-hour {
background: white !important; background: white !important;
} }
.day-of-week{ .day-of-week {
text-transform: uppercase; text-transform: uppercase;
} }
@ -43,50 +45,51 @@
font-size: 13px; font-size: 13px;
} }
.handsontable td, .handsontable th{ .handsontable td,
.handsontable th {
font-family: monospace; font-family: monospace;
color: var(--white); color: var(--white);
background: var(--black); background: var(--black);
} }
th{ th {
color: gray !important; color: gray !important;
} }
td.color-black{ td.color-black {
background: var(--black); background: var(--black);
} }
td.color-red{ td.color-red {
background: var(--red); background: var(--red);
} }
td.color-blue{ td.color-blue {
background: var(--blue); background: var(--blue);
} }
td.color-green{ td.color-green {
background: var(--green); background: var(--green);
} }
td.color-purple{ td.color-purple {
background: var(--purple); background: var(--purple);
} }
td.color-lime{ td.color-lime {
background: var(--lime); background: var(--lime);
color: var(--black); color: var(--black);
} }
td.color-cyan{ td.color-cyan {
background: var(--cyan); background: var(--cyan);
} }
td.color-darkred{ td.color-darkred {
background: var(--darkred); background: var(--darkred);
} }
td.color-pink{ td.color-pink {
background: var(--pink); background: var(--pink);
} }
td.color-orange{ td.color-orange {
background: var(--orange); background: var(--orange);
} }
td.color-yellow{ td.color-yellow {
background: var(--yellow); background: var(--yellow);
color: var(--black); color: var(--black);
} }
h2{ h2 {
color:green; color: green;
} }

View File

@ -1,4 +1,4 @@
@import './base.css'; @import "./base.css";
#app { #app {
padding: 1em 2rem; padding: 1em 2rem;

View File

@ -8,9 +8,7 @@ storeSession.connect();
<template> <template>
<div v-if="!storeSession.isConnected"> <div v-if="!storeSession.isConnected">
<button @click="storeSession.loginAsRyan()"> <button @click="storeSession.loginAsRyan()">Connect</button>
Connect
</button>
</div> </div>
<div v-if="storeSession.isConnected"> <div v-if="storeSession.isConnected">
Connected as {{ storeSession.session.userId }}. Connected as {{ storeSession.session.userId }}.

View File

@ -1,20 +0,0 @@
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
</style>

View File

@ -1,13 +0,0 @@
<script setup lang="ts">
defineProps<{
year: string
}>()
</script>
<template>
<h1 class="green">{{ year }} Tracker Expanded</h1>
</template>
<style scoped>
</style>

View File

@ -1,86 +0,0 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>

View File

@ -1,13 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico"> <link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Life Tracker Expanded</title> <title>Life Tracker Expanded</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="main.ts"></script>
</body> </body>
</html> </html>

View File

@ -1,12 +1,12 @@
import { createApp } from 'vue'; import { createApp } from "vue";
import { createPinia } from 'pinia'; import { createPinia } from "pinia";
import App from './App.vue'; import App from "./App.vue";
import router from './router'; import router from "./router";
import './assets/main.css'; import "./assets/main.css";
const pinia = createPinia(); const pinia = createPinia();
const app = createApp(App).use(router).use(pinia); const app = createApp(App).use(router).use(pinia);
app.mount('#app'); app.mount("#app");

View File

@ -1,39 +1,25 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from "vue-router";
import TableView from '../views/TableView.vue' import TableView from "../views/TableView.vue";
import DatabaseView from '../views/DatabaseView.vue' import DatabaseView from "../views/DatabaseView.vue";
import AboutView from '../views/AboutView.vue'
import { useSessionStore } from "../stores/session";
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: [ routes: [
{ {
path: '/', path: "/",
redirect: '/table' redirect: "/table",
}, },
{ {
path: '/table', path: "/table",
name: 'table', name: "table",
component: TableView component: TableView,
}, },
{ {
path: '/database', path: "/database",
name: 'database', name: "database",
component: DatabaseView component: DatabaseView,
} },
] ],
}) });
export default router export default router;
// router.beforeEach(async (to) => {
// // redirect to login page if not logged in and trying to access a restricted page
// const publicPages = ['/database'];
// const authRequired = !publicPages.includes(to.path);
// const auth = useSessionStore();
// if (authRequired && !auth.session['id']) {
// return '/database';
// }
// });

View File

@ -1,7 +1,7 @@
import axios from 'axios' import axios from "axios";
export default() => { export default () => {
return axios.create({ return axios.create({
baseURL: `http://localhost:8081` baseURL: `http://localhost:8081`,
}) });
} };

View File

@ -2,83 +2,98 @@ import { Client, Databases, Account, Query } from "appwrite";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
class AppwriteService { class AppwriteService {
databaseId = 'lifetracker-db'; databaseId = "lifetracker-db";
collectionId = "ryan"; collectionId = "ryan";
constructor() { constructor() {
this.appwrite = new Client().setEndpoint("http://ryanpandya.com:8080/v1").setProject("lifetracker") this.appwrite = new Client()
.setEndpoint("http://ryanpandya.com:8080/v1")
.setProject("lifetracker");
this.database = new Databases(this.appwrite); this.database = new Databases(this.appwrite);
this.account = new Account(this.appwrite); this.account = new Account(this.appwrite);
} }
subscribe = (callback) => { subscribe = (callback) => {
return this.appwrite.subscribe('databases.lifetracker-db.collections.ryan.documents', callback); return this.appwrite.subscribe(
"databases.lifetracker-db.collections.ryan.documents",
callback
);
// "databases.${this.databaseId}.collections.${this.collectionId}.documents", callback); // "databases.${this.databaseId}.collections.${this.collectionId}.documents", callback);
} };
addEntry = async ({ date, hours, mood, comments }) => { addEntry = async ({ date, hours, mood, comments }) => {
await this.database.createDocument(this.databaseId, this.collectionId, await this.database.createDocument(
this.databaseId,
this.collectionId,
date, date,
{ date: new Date(date), hours: hours, mood: mood, comments: comments }); { date: new Date(date), hours, mood, comments }
} );
};
deleteEntry = async (entryId) => { deleteEntry = async (entryId) => {
await this.database.deleteDocument(this.databaseId, this.collectionId, entryId); await this.database.deleteDocument(
} this.databaseId,
this.collectionId,
entryId
);
};
updateEntry = async ({ date, hours, mood, comments }) => { updateEntry = async ({ date, hours, mood, comments }) => {
var hours = JSON.parse("[" + hours + "]") hours = JSON.parse("[" + hours + "]");
await this.database.updateDocument(this.databaseId, this.collectionId, await this.database.updateDocument(
this.databaseId,
this.collectionId,
date, date,
{ date: new Date(date), hours: hours, mood: mood, comments: comments }); { date: new Date(date), hours, mood, comments }
} );
};
getUser = async () => { getUser = async () => {
return await this.account.get(); return await this.account.get();
} };
login = async () => { login = async () => {
await this.account.createAnonymousSession(); await this.account.createAnonymousSession();
return await this.getUser(); return await this.getUser();
} };
getEntries = async (date=null, numEntries=null) => { getEntries = async (date = null, numEntries = null) => {
if(date == null){ if (date == null) {
date = DateTime.fromObject({ date = DateTime.fromObject({
year: DateTime.now().toFormat("y"), year: DateTime.now().toFormat("y"),
month: 1, month: 1,
day: 2 day: 2,
}); });
} } else {
else{
date = DateTime.fromISO(date); date = DateTime.fromISO(date);
} }
var firstEntry = (await this.database.listDocuments( const firstEntry = (
this.databaseId, this.collectionId, await this.database.listDocuments(this.databaseId, this.collectionId, [
[Query.orderAsc("date"),Query.limit(1)]) Query.orderAsc("date"),
Query.limit(1),
])
).documents[0]; ).documents[0];
var referenceDate = DateTime.fromISO(firstEntry.date).toUTC(); const referenceDate = DateTime.fromISO(firstEntry.date).toUTC();
var offset = Math.floor(date.diff(referenceDate).as("days")) - 1; const offset = Math.floor(date.diff(referenceDate).as("days")) - 1;
if (numEntries == null) { if (numEntries == null) {
numEntries = Math.floor(DateTime.now().diff(referenceDate).as("days")) + 7; numEntries =
Math.floor(DateTime.now().diff(referenceDate).as("days")) + 7;
} }
return (await this.database.listDocuments( return (
this.databaseId, this.collectionId, await this.database.listDocuments(this.databaseId, this.collectionId, [
[
Query.orderAsc("date"), Query.orderAsc("date"),
Query.offset(offset), Query.offset(offset),
Query.limit(numEntries) Query.limit(numEntries),
] ])
)
).documents; ).documents;
} };
logout = () => { logout = () => {
return this.appwrite.account.deleteSession('current'); return this.appwrite.account.deleteSession("current");
} };
} }
export default new AppwriteService() export default new AppwriteService();

View File

@ -1,23 +1,23 @@
import type { IEntry } from "@/types/entry"; import type { IEntry } from "@/types/entry";
import { defineStore } from 'pinia'; import { defineStore } from "pinia";
import Api from "@/services/Api" import Api from "@/services/Api";
export const useDatabaseStore = defineStore({ export const useDatabaseStore = defineStore({
id: 'databaseState', id: "databaseState",
state: () => ({ state: () => ({
entries: [] as IEntry[] entries: [] as IEntry[],
}), }),
getters: { getters: {
length: (state) => state.entries.length length: (state) => state.entries.length,
}, },
actions: { actions: {
async fetchEntries(){ async fetchEntries() {
try{ try {
const response = await Api().get("database"); const response = await Api().get("database");
this.entries = response.data; this.entries = response.data;
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} }
} },
} },
}) });

View File

@ -1,34 +1,33 @@
import type { ISession } from '@/types/session'; import type { ISession } from "@/types/session";
import { defineStore } from 'pinia'; import { defineStore } from "pinia";
import { Client, Account, ID } from 'appwrite'; import { Client, Account } from "appwrite";
const appwriteclient = new Client() const appwriteclient = new Client()
.setEndpoint('http://ryanpandya.com:8080/v1') .setEndpoint("http://ryanpandya.com:8080/v1")
.setProject('lifetracker'); .setProject("lifetracker");
const account = new Account(appwriteclient); const account = new Account(appwriteclient);
export const useSessionStore = defineStore({ export const useSessionStore = defineStore({
id: 'sessionState', id: "sessionState",
state: () => ({ state: () => ({
session: [] as ISession[], session : {} as ISession
}), }),
getters: { getters: {
isConnected: (state) => state.session['userId'], isConnected: (state) => state.session.userId,
}, },
actions: { actions: {
logout() { logout() {
const promise = account.deleteSession(this.session['id']); const promise = account.deleteSession(this.session.id);
var self = this; const self = this;
promise.then( promise.then(
function (response) { function (response) {
self.session = [ self.session =
{ {
email: '', email: "",
userId: '', userId: "",
id: '', id: "",
}, }
];
console.log(response); // Success console.log(response); // Success
}, },
function (error) { function (error) {
@ -37,52 +36,49 @@ export const useSessionStore = defineStore({
); );
}, },
connect() { connect() {
const promise = account.getSession('current'); const promise = account.getSession("current");
var session: ISession = { const session: ISession = {
email: '', email: "",
userId: '', userId: "",
id: '', id: "",
}; };
var self = this; const self = this;
promise.then( promise.then(
function (response) { function (response) {
session.email = response.providerUid; session.email = response.providerUid;
session.userId = response.userId; session.userId = response.userId;
session.id = response.$id; session.id = response.$id;
console.log('Connected to existing session'); console.log("Connected to existing session");
self.session = session; self.session = session;
}, },
function (error) { function (error) {
session.email = null; console.log("No existing session; starting fresh.");
session.userId = null;
session.id = '';
console.log('No existing session; starting fresh.');
self.session = session; self.session = session;
} }
); );
}, },
loginAsRyan() { loginAsRyan() {
this.login('ryan@ryanpandya.com', 'A(84)o9@38appwrite'); this.login("ryan@ryanpandya.com", "A(84)o9@38appwrite");
}, },
login(email: string, password: string) { login(email: string, password: string) {
console.log(this.session); console.log(this.session);
const promise = account.createEmailSession(email, password); const promise = account.createEmailSession(email, password);
var session: ISession = { const session: ISession = {
email: '', email: "",
userId: '', userId: "",
id: '', id: "",
}; };
var self = this; const self = this;
promise.then( promise.then(
function (response) { function (response) {
session.email = response.providerUid; session.email = response.providerUid;
session.userId = response.userId; session.userId = response.userId;
session.id = response.$id; session.id = response.$id;
console.log('Logged in'); console.log("Logged in");
self.session = session; self.session = session;
}, },
function (error) { function (error) {
console.log('Error'); console.log("Error");
} }
); );
}, },

View File

@ -1,6 +1,6 @@
export interface IEntry { export interface IEntry {
date: Date, date: Date;
hours: Number[], hours: Number[];
mood: Number, mood: Number;
note: String note: String;
} }

View File

@ -1,33 +1,33 @@
<script> <script>
import { defineComponent } from 'vue'; import { defineComponent } from "vue";
import { HotTable } from '@handsontable/vue3'; import { HotTable } from "@handsontable/vue3";
import { registerAllModules } from 'handsontable/registry'; import { registerAllModules } from "handsontable/registry";
import 'handsontable/dist/handsontable.full.css'; import "handsontable/dist/handsontable.full.css";
// register Handsontable's modules // register Handsontable's modules
registerAllModules(); registerAllModules();
const ExampleComponent = defineComponent({ const ExampleComponent = defineComponent({
components: {
HotTable,
},
data() { data() {
return { return {
hotSettings: { hotSettings: {
data: [ data: [
['A1', 'B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1', 'J1'], ["A1", "B1", "C1", "D1", "E1", "F1", "G1", "H1", "I1", "J1"],
['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2', 'J2'], ["A2", "B2", "C2", "D2", "E2", "F2", "G2", "H2", "I2", "J2"],
['A3', 'B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3', 'J3'], ["A3", "B3", "C3", "D3", "E3", "F3", "G3", "H3", "I3", "J3"],
['A4', 'B4', 'C4', 'D4', 'E4', 'F4', 'G4', 'H4', 'I4', 'J4'], ["A4", "B4", "C4", "D4", "E4", "F4", "G4", "H4", "I4", "J4"],
['A5', 'B5', 'C5', 'D5', 'E5', 'F5', 'G5', 'H5', 'I5', 'J5'], ["A5", "B5", "C5", "D5", "E5", "F5", "G5", "H5", "I5", "J5"],
['A6', 'B6', 'C6', 'D6', 'E6', 'F6', 'G6', 'H6', 'I6', 'J6'], ["A6", "B6", "C6", "D6", "E6", "F6", "G6", "H6", "I6", "J6"],
], ],
colHeaders: true, colHeaders: true,
height: 'auto', height: "auto",
licenseKey: 'non-commercial-and-evaluation' licenseKey: "non-commercial-and-evaluation",
} },
}; };
}, },
components: {
HotTable,
}
}); });
export default ExampleComponent; export default ExampleComponent;
@ -38,5 +38,4 @@ export default ExampleComponent;
</div> </div>
</template> </template>
<style> <style></style>
</style>

View File

@ -1,34 +1,32 @@
<script setup> <script setup>
import { useDatabaseStore } from "@/stores/database" import appwrite from "@/services/appwrite";
import Api from "@/services/Api"
import appwrite from '@/services/appwrite';
import { DateTime } from "luxon"; import { DateTime } from "luxon";
</script> </script>
<script> <script>
import { defineComponent, toRaw, reactive } from 'vue'; import { defineComponent } from "vue";
import { HotTable } from '@handsontable/vue3'; import { HotTable } from "@handsontable/vue3";
import { registerAllModules } from 'handsontable/registry'; import { registerAllModules } from "handsontable/registry";
import 'handsontable/dist/handsontable.full.css'; import "handsontable/dist/handsontable.full.css";
import '../assets/colors.css'; import "../assets/colors.css";
import Handsontable from 'handsontable/base';
// register Handsontable's modules // register Handsontable's modules
registerAllModules(); registerAllModules();
function debounce(func, wait, immediate) { // function debounce(func, wait, immediate) {
var timeout; // let timeout;
return function() { // return function () {
var context = this, args = arguments; // const context = this;
var later = function() { // const args = arguments;
timeout = null; // const later = function () {
if (!immediate) func.apply(context, args); // timeout = null;
}; // if (!immediate) func.apply(context, args);
var callNow = immediate && !timeout; // };
clearTimeout(timeout); // const callNow = immediate && !timeout;
timeout = setTimeout(later, wait); // clearTimeout(timeout);
if (callNow) func.apply(context, args); // timeout = setTimeout(later, wait);
}; // if (callNow) func.apply(context, args);
}; // };
// }
function getHourClass(i) { function getHourClass(i) {
const colorMapping = { const colorMapping = {
@ -42,15 +40,14 @@ function getHourClass(i) {
7: "orange", 7: "orange",
8: "purple", 8: "purple",
9: "darkred", 9: "darkred",
10: "lime" 10: "lime",
} };
if(i > 10){ if (i > 10) {
i = Math.ceil(i/10) - 1 i = Math.ceil(i / 10) - 1;
} }
if (i == null) { if (i == null) {
return ""; return "";
} } else {
else {
return "color-" + colorMapping[i]; return "color-" + colorMapping[i];
} }
} }
@ -58,21 +55,24 @@ function getHourClass(i) {
function hourCol(t) { function hourCol(t) {
return { return {
data: "hours." + t, data: "hours." + t,
type: 'numeric', type: "numeric",
renderer(instance, td, row, col, prop, value) { renderer(instance, td, row, col, prop, value) {
td.className = getHourClass(value); td.className = getHourClass(value);
td.innerText = value == null ? value : parseFloat(value); td.innerText = value == null ? value : parseFloat(value);
return td; return td;
} },
} };
} }
const colHeaders = ["DATE", "DAY", "12 AM"] const colHeaders = ["DATE", "DAY", "12 AM"]
.concat(Array.from(new Array(11), (x, y) => y+1 + " AM") ) .concat(Array.from(new Array(11), (x, y) => y + 1 + " AM"))
.concat(Array.from(new Array(12), (x, y) => y+1 + " PM") ) .concat(Array.from(new Array(12), (x, y) => y + 1 + " PM"))
.concat(["Mood", "Comments"]) .concat(["Mood", "Comments"]);
const ExampleComponent = defineComponent({ const ExampleComponent = defineComponent({
components: {
HotTable,
},
data() { data() {
return { return {
entries: [], entries: [],
@ -85,228 +85,248 @@ const ExampleComponent = defineComponent({
readOnly: true, readOnly: true,
data: "date", data: "date",
renderer(instance, td, row, col, prop, value) { renderer(instance, td, row, col, prop, value) {
var date = DateTime.fromISO(value).toUTC().toFormat("MM/dd"); const date = DateTime.fromISO(value).toUTC().toFormat("MM/dd");
var today = DateTime.now().toFormat("MM/dd"); const today = DateTime.now().toFormat("MM/dd");
if(today == date){ if (today === date) {
td.className = "color-black invert"; td.className = "color-black invert";
} else {
td.className = "color-black";
} }
else{td.className = "color-black";}
td.innerText = date; td.innerText = date;
return td; return td;
} },
}, },
{ {
readOnly: true, readOnly: true,
data: "date", data: "date",
renderer(instance, td, row, col, prop, value) { renderer(instance, td, row, col, prop, value) {
var date = DateTime.fromISO(value).toUTC(); const date = DateTime.fromISO(value).toUTC();
var today = DateTime.now().toFormat("MM/dd"); const today = DateTime.now().toFormat("MM/dd");
if(date.toFormat("MM/dd") == today){ if (date.toFormat("MM/dd") === today) {
td.className = "color-black invert day-of-week"; td.className = "color-black invert day-of-week";
} else {
td.className = "color-black day-of-week";
} }
else{td.className = "color-black day-of-week";}
td.innerText = date.toFormat("EEE"); td.innerText = date.toFormat("EEE");
return td; return td;
} },
}, },
Array.from(new Array(24), (_, t) => hourCol(t)), Array.from(new Array(24), (_, t) => hourCol(t)),
{ data: "mood", type: "numeric" }, { data: "mood", type: "numeric" },
{ data: "comments", className: "align-left" } { data: "comments", className: "align-left" },
].flat(), ].flat(),
colHeaders: colHeaders, colHeaders,
rowHeaders: false, rowHeaders: false,
readOnly: false, readOnly: false,
width: '100%', width: "100%",
height: '100%', height: "100%",
rowHeights: '22px', rowHeights: "22px",
colWidths(i) { colWidths(i) {
if((i > 1) && (i < 26)){ if (i > 1 && i < 26) {
return "50px"; return "50px";
} } else if (i === 27) {
else if(i == 27){
return "1000px"; return "1000px";
} } else {
else{
return "50px"; return "50px";
} }
}, },
afterSelection: (row, column, row2, column2, preventScrolling, selectionLayerLevel) => { afterSelection: (
row,
}, column,
row2,
column2,
preventScrolling,
selectionLayerLevel
) => {},
afterChange: (changes) => { afterChange: (changes) => {
this.fixSelectionBug(); this.fixSelectionBug();
//this.setActiveHourHeader(); // this.setActiveHourHeader();
if (changes != null) { if (changes != null) {
var entry = this.entries[changes[0][0]]; const entry = this.entries[changes[0][0]];
entry.date = entry.date.replace(/T.*/, ""); entry.date = entry.date.replace(/T.*/, "");
appwrite.updateEntry(entry); appwrite.updateEntry(entry);
} }
}, },
licenseKey: 'non-commercial-and-evaluation' licenseKey: "non-commercial-and-evaluation",
}, },
hotRef: null hotRef: null,
} };
}, },
async mounted(){ async mounted() {
this.user = await appwrite.getUser(); this.user = await appwrite.getUser();
this.entries = await appwrite.getEntries(); this.entries = await appwrite.getEntries();
this.updateTable(); this.updateTable();
this.subscribe(); this.subscribe();
this.fixSelectionBug(); this.fixSelectionBug();
//this.lazyLoadEntries(); // this.lazyLoadEntries();
this.scrollToEnd(); this.scrollToEnd();
this.selectCurrentCell(); this.selectCurrentCell();
}, },
created () { created() {
// window.addEventListener('wheel', debounce(this.handleScroll, 500, true)); // window.addEventListener('wheel', debounce(this.handleScroll, 500, true));
}, },
unmounted () { unmounted() {
// window.removeEventListener('wheel', debounce(this.handleScroll, 500, true)); // window.removeEventListener('wheel', debounce(this.handleScroll, 500, true));
}, },
updated() { updated() {
this.scrollToEnd(); this.scrollToEnd();
}, },
methods: { methods: {
selectCurrentCell(){ selectCurrentCell() {
var nowRow = this.entries.findIndex((e) => const nowRow =
DateTime.fromISO(e.date).toISODate() == DateTime.now().toISODate()) - 1; this.entries.findIndex(
var nowCol = parseInt(DateTime.now().toFormat("HH")) + 1; (e) =>
DateTime.fromISO(e.date).toISODate() === DateTime.now().toISODate()
) - 1;
const nowCol = parseInt(DateTime.now().toFormat("HH")) + 1;
this.hotRef.selectCell(nowRow, nowCol); this.hotRef.selectCell(nowRow, nowCol);
}, },
scrollToEnd() { scrollToEnd() {
this.hotRef.scrollViewportTo( this.hotRef.scrollViewportTo(this.entries.length - 10);
this.entries.length - 10
);
}, },
async lazyLoadEntries(){ async lazyLoadEntries() {
var earliestDate = DateTime.fromObject({ const earliestDate = DateTime.fromObject({
year: DateTime.now().toFormat("y"), year: DateTime.now().toFormat("y"),
month: 1, month: 1,
day: 1 day: 1,
}); });
var numEntries = Math.floor(DateTime.now().diff(earliestDate).as("days")); const numEntries = Math.floor(
console.log("Grabbing " + numEntries + " entries starting from " + earliestDate.toISODate()); DateTime.now().diff(earliestDate).as("days")
var newEntries = await appwrite.getEntries( );
earliestDate.plus({days: 1}), console.log(
numEntries); "Grabbing " +
var entries = this.entries; numEntries +
" entries starting from " +
earliestDate.toISODate()
);
const newEntries = await appwrite.getEntries(
earliestDate.plus({ days: 1 }),
numEntries
);
const entries = this.entries;
newEntries.reverse().forEach((e) => { newEntries.reverse().forEach((e) => {
entries.unshift(e); entries.unshift(e);
}) });
this.entries = entries; this.entries = entries;
this.hotRef.scrollViewportTo(129); this.hotRef.scrollViewportTo(129);
}, },
setActiveHourHeader(){ setActiveHourHeader() {
var timeString = DateTime.now().toFormat("h a"); const timeString = DateTime.now().toFormat("h a");
console.log(timeString); console.log(timeString);
var moot = Array.from(document.querySelectorAll("th")); const moot = Array.from(document.querySelectorAll("th"));
var poot = moot.filter((e) => { return e.innerText == timeString}); const poot = moot.filter((e) => {
return e.innerText === timeString;
});
poot[0].classList.add("active-hour"); poot[0].classList.add("active-hour");
}, },
async handleScroll(e){ async handleScroll(e) {
if(e.deltaY > 0){ if (e.deltaY > 0) {
var lastDate = DateTime.fromISO( const lastDate = DateTime.fromISO(
this.entries[ this.entries[this.hotRef.getPlugin("autoRowSize").getLastVisibleRow()]
this.hotRef .date
.getPlugin('autoRowSize') )
.getLastVisibleRow() .toUTC()
].date).toUTC().plus({ days: 1 }); .plus({ days: 1 });
console.log("Grabbing 5 more entries starting at " + lastDate.toISO()); console.log("Grabbing 5 more entries starting at " + lastDate.toISO());
var newEntries = await appwrite.getEntries( const newEntries = await appwrite.getEntries(
lastDate.plus({ days: 1 }) lastDate.plus({ days: 1 }),
, 5); 5
);
newEntries.forEach((e) => { newEntries.forEach((e) => {
this.entries.push(e); this.entries.push(e);
}) });
} } else {
else{ const firstDate = DateTime.fromISO(
var firstDate = DateTime.fromISO(
this.entries[ this.entries[
this.hotRef this.hotRef.getPlugin("autoRowSize").getFirstVisibleRow()
.getPlugin('autoRowSize') ].date
.getFirstVisibleRow() )
].date).toUTC().minus({ days: 1 }); .toUTC()
console.log("Grabbing 5 previous entries ending at " + firstDate.toISO()); .minus({ days: 1 });
var newEntries = await appwrite.getEntries( console.log(
firstDate.minus({ days: 3 }) "Grabbing 5 previous entries ending at " + firstDate.toISO()
, 5); );
var entries = this.entries; const newEntries = await appwrite.getEntries(
firstDate.minus({ days: 3 }),
5
);
const entries = this.entries;
newEntries.reverse().forEach((e) => { newEntries.reverse().forEach((e) => {
entries.unshift(e); entries.unshift(e);
}) });
this.entries = entries; this.entries = entries;
} }
}, },
fixSelectionBug(){ fixSelectionBug() {
if(this.hotRef){ if (this.hotRef) {
var offset = (this.hotRef.getRowHeight()) * (this.entries.length + 1) + 2;//document.querySelector(".wtHider").clientHeight; const offset =
document.querySelector(".htBorders div").style.top = "-" + offset + "px"; this.hotRef.getRowHeight() * (this.entries.length + 1) + 2; // document.querySelector(".wtHider").clientHeight;
document.querySelector(".htBorders div").style.top =
"-" + offset + "px";
} }
}, },
subscribe(){ subscribe() {
console.log("Subscribing to realtime."); console.log("Subscribing to realtime.");
appwrite.subscribe((payload) => { appwrite.subscribe((payload) => {
var event = payload.events.filter((e) => const event = payload.events
.filter((e) =>
e.match(/databases\.\*\.collections\.\*\.documents\.\*\.\w+/) e.match(/databases\.\*\.collections\.\*\.documents\.\*\.\w+/)
)[0].replace(/.+\./,""); )[0]
.replace(/.+\./, "");
switch (event) { switch (event) {
case 'create': case "create":
this.entries.push(payload.payload) this.entries.push(payload.payload);
this.entries = this.entries;
this.updateTable(); this.updateTable();
break break;
case 'update': case "update":
console.log("Updating"); console.log("Updating");
this.entries = this.entries.map((day) => { this.entries = this.entries.map((day) => {
if (day.$id === payload.payload.$id) { if (day.$id === payload.payload.$id) {
return payload.payload return payload.payload;
} else { } else {
return day return day;
} }
}); });
this.updateTable(); this.updateTable();
break break;
case 'delete': case "delete":
this.entries = this.entries.filter((day) => day.$id !== payload.payload.$id); this.entries = this.entries.filter(
(day) => day.$id !== payload.payload.$id
);
this.updateTable(); this.updateTable();
break break;
} }
}) });
}, },
updateTable(){ updateTable() {
this.hotRef = this.$refs.wrapper.hotInstance; this.hotRef = this.$refs.wrapper.hotInstance;
this.hotRef.loadData(this.entries); this.hotRef.loadData(this.entries);
}, },
rewrite(d){ rewrite(d) {
var emptyEntry = { const emptyEntry = {
date: d.toISODate(), date: d.toISODate(),
hours: [], hours: [],
mood: null, mood: null,
comments: "" comments: "",
} };
appwrite.updateEntry(emptyEntry); appwrite.updateEntry(emptyEntry);
console.log("Updated " + d.toISODate()); console.log("Updated " + d.toISODate());
}, },
rewriteEntries(){ rewriteEntries() {
const startDate = DateTime.fromISO("2023-12-10"); const startDate = DateTime.fromISO("2023-12-10");
const endDate = DateTime.fromISO("2023-12-31"); const endDate = DateTime.fromISO("2023-12-31");
for(var d = startDate; d <= endDate; d = d.plus({days: 1})){ for (let d = startDate; d <= endDate; d = d.plus({ days: 1 })) {
setTimeout(this.rewrite(d), 500); setTimeout(this.rewrite(d), 500);
} }
}
}, },
components: { },
HotTable,
}
}); });
export default ExampleComponent; export default ExampleComponent;
</script> </script>
<template> <template>
<button @click="this.setActiveHourHeader()">Fuck</button> <!-- <div>{{ entries }}</div>
<!-- <div>{{ entries }}</div>
<ul> <ul>
<li v-for="e in entries" :key="e"> <li v-for="e in entries" :key="e">
@ -314,10 +334,12 @@ export default ExampleComponent;
</li> </li>
</ul> --> </ul> -->
<div id="table"> <div id="table">
<hot-table ref="wrapper" :settings="hotSettings" :data="entries"></hot-table> <hot-table
ref="wrapper"
:settings="hotSettings"
:data="entries"
></hot-table>
</div> </div>
</template> </template>
<style> <style></style>
</style>

View File

@ -6,7 +6,9 @@
"paths": { "paths": {
"@/*": ["./src/*"] "@/*": ["./src/*"]
}, },
"noImplicitAny": false "noImplicitAny": false,
"ignoreDeprecations": "5.0",
"allowJs": true
}, },
"references": [ "references": [

View File

@ -1,8 +1,14 @@
{ {
"extends": "@vue/tsconfig/tsconfig.node.json", "extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"], "include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"playwright.config.*"
],
"compilerOptions": { "compilerOptions": {
"composite": true, "composite": true,
"types": ["node"] "types": ["node"],
"ignoreDeprecations": "5.0"
} }
} }

View File

@ -1,14 +1,24 @@
import { fileURLToPath, URL } from 'node:url' import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import { defineConfig } from 'vite' import eslintPlugin from "vite-plugin-eslint";
import vue from '@vitejs/plugin-vue' import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) "@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
root: "src",
build: {
outDir: "../../lifetracker-server/dist",
emptyOutDir: true,
},
server: {
hmr: {
overlay: false
} }
} }
}) });