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(bodyParser.json())
app.use(cors())
app.get('/database', (req, res) => {
const promise = databases.listDocuments('lifetracker-db', 'ryan');
promise.then(function ({ documents } = response) {
// app.get('/database', (req, res) => {
// const promise = databases.listDocuments('lifetracker-db', 'ryan');
// promise.then(function ({ documents } = response) {
res.send(documents)
}, function (error) {
console.log(error);
});
})
// res.send(documents)
// }, function (error) {
// console.log(error);
// });
// })
app.get('/entry/:date', (req, res) => {
var date = new Date(req.params['date']).toISOString().replace(/T.*/,"");
console.log("Fetching entry for " + date);
// app.get('/entry/:date', (req, res) => {
// var date = new Date(req.params['date']).toISOString().replace(/T.*/,"");
// console.log("Fetching entry for " + date);
const promise = databases.listDocuments('lifetracker-db', 'ryan');
promise.then(function ({ documents } = response) {
// const promise = databases.listDocuments('lifetracker-db', 'ryan');
// promise.then(function ({ documents } = response) {
entries = documents.map(e => e.Date)
res.send(entries)
}, function (error) {
console.log(error);
});
})
// entries = documents.map(e => e.Date)
// res.send(entries)
// }, function (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)

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);
function importCSV(){
console.log("Worked.");
}
var data = fs.readFileSync('data.csv')
function importsCSV(){
var data = fs.readFileSync('data.csv')
.toString() // convert Buffer to string
.split('\n') // split string to lines
.slice(1) // remove first line
@ -41,5 +45,6 @@ var data = fs.readFileSync('data.csv')
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:
1. Disable the built-in TypeScript Extension
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)`
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. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
## Customize configuration

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,9 +8,7 @@ storeSession.connect();
<template>
<div v-if="!storeSession.isConnected">
<button @click="storeSession.loginAsRyan()">
Connect
</button>
<button @click="storeSession.loginAsRyan()">Connect</button>
</div>
<div v-if="storeSession.isConnected">
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>
<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">
<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>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="module" src="main.ts"></script>
</body>
</html>

View File

@ -1,12 +1,12 @@
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { createApp } from "vue";
import { createPinia } from "pinia";
import App from './App.vue';
import router from './router';
import App from "./App.vue";
import router from "./router";
import './assets/main.css';
import "./assets/main.css";
const pinia = createPinia();
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 TableView from '../views/TableView.vue'
import DatabaseView from '../views/DatabaseView.vue'
import AboutView from '../views/AboutView.vue'
import { useSessionStore } from "../stores/session";
import { createRouter, createWebHistory } from "vue-router";
import TableView from "../views/TableView.vue";
import DatabaseView from "../views/DatabaseView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
redirect: '/table'
path: "/",
redirect: "/table",
},
{
path: '/table',
name: 'table',
component: TableView
path: "/table",
name: "table",
component: TableView,
},
{
path: '/database',
name: 'database',
component: DatabaseView
}
]
})
path: "/database",
name: "database",
component: DatabaseView,
},
],
});
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';
// }
// });
export default router;

View File

@ -1,7 +1,7 @@
import axios from 'axios'
import axios from "axios";
export default() => {
export default () => {
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";
class AppwriteService {
databaseId = 'lifetracker-db';
databaseId = "lifetracker-db";
collectionId = "ryan";
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.account = new Account(this.appwrite);
}
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);
}
};
addEntry = async ({ date, hours, mood, comments }) => {
await this.database.createDocument(this.databaseId, this.collectionId,
await this.database.createDocument(
this.databaseId,
this.collectionId,
date,
{ date: new Date(date), hours: hours, mood: mood, comments: comments });
}
{ date: new Date(date), hours, mood, comments }
);
};
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 }) => {
var hours = JSON.parse("[" + hours + "]")
await this.database.updateDocument(this.databaseId, this.collectionId,
hours = JSON.parse("[" + hours + "]");
await this.database.updateDocument(
this.databaseId,
this.collectionId,
date,
{ date: new Date(date), hours: hours, mood: mood, comments: comments });
}
{ date: new Date(date), hours, mood, comments }
);
};
getUser = async () => {
return await this.account.get();
}
};
login = async () => {
await this.account.createAnonymousSession();
return await this.getUser();
}
};
getEntries = async (date=null, numEntries=null) => {
if(date == null){
getEntries = async (date = null, numEntries = null) => {
if (date == null) {
date = DateTime.fromObject({
year: DateTime.now().toFormat("y"),
month: 1,
day: 2
day: 2,
});
}
else{
} else {
date = DateTime.fromISO(date);
}
var firstEntry = (await this.database.listDocuments(
this.databaseId, this.collectionId,
[Query.orderAsc("date"),Query.limit(1)])
const firstEntry = (
await this.database.listDocuments(this.databaseId, this.collectionId, [
Query.orderAsc("date"),
Query.limit(1),
])
).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) {
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(
this.databaseId, this.collectionId,
[
return (
await this.database.listDocuments(this.databaseId, this.collectionId, [
Query.orderAsc("date"),
Query.offset(offset),
Query.limit(numEntries)
]
)
Query.limit(numEntries),
])
).documents;
}
};
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 { defineStore } from 'pinia';
import Api from "@/services/Api"
import { defineStore } from "pinia";
import Api from "@/services/Api";
export const useDatabaseStore = defineStore({
id: 'databaseState',
id: "databaseState",
state: () => ({
entries: [] as IEntry[]
entries: [] as IEntry[],
}),
getters: {
length: (state) => state.entries.length
length: (state) => state.entries.length,
},
actions: {
async fetchEntries(){
try{
async fetchEntries() {
try {
const response = await Api().get("database");
this.entries = response.data;
} catch (error) {
console.log(error);
}
}
}
})
},
},
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,14 @@
{
"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": {
"composite": true,
"types": ["node"]
"types": ["node"],
"ignoreDeprecations": "5.0"
}
}

View File

@ -1,14 +1,24 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import eslintPlugin from "vite-plugin-eslint";
import vue from "@vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
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
}
}
})
});