Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature UI #21

Merged
merged 4 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>HealthRecord</title>
<meta name="description" content="Record, track, and visualize your personal health data.">
<meta name="theme-color" content="rgb(229, 231, 235)">
Expand Down
52 changes: 0 additions & 52 deletions public/manifest.webmanifest

This file was deleted.

46 changes: 18 additions & 28 deletions src/components/Dashboard/Dashboard.vue
Original file line number Diff line number Diff line change
@@ -1,40 +1,30 @@
<script setup>
import { people } from '../../store/people';
import { vitals } from '../../store/vitals';
import { measurements } from '../../store/measurements';
import { ChevronRightIcon } from '@heroicons/vue/24/outline';
import pluralize from 'pluralize';
import PersonListItem from '../People/PersonListItem.vue';
import { scrolled } from '../../store/ui';
</script>

<template>
<div >
<div class="sticky top-0 pt-0 mt-0 pb-5 grid grid-flow-col items-start bg-gradient-to-b from-gray-200 from-90%">
<header>
<header
class="sticky grid grid-cols-[auto_min-content] top-0 p-4 py-2 sm:pt-4 bg-gray-200/70 backdrop-blur-xl border-b border-transparent transition-all"
:class="{'!border-gray-300': scrolled }"
>
<hgroup>
<h2 class="text-2xl font-bold">Dashboard</h2>
<p class="text-sm text-gray-500">HealthRecord data overview.</p>
</header>
</div>
<div class="grid grid-rows-2 grid-cols-2 sm:grid-cols-3 xl:grid-cols-4 gap-3">
<div class="group grid grid-cols-[auto_min-content] min-h-[10em] bg-gray-50 p-3 rounded-xl cursor-pointer shadow-sm hover:shadow hover:bg-white transition-all" @click="$router.push({ name: 'People' })">
<div class="grid">
<h3 class="font-semibold mb-1">People</h3>
<p class="text-gray-500 text-sm self-end">You are tracking {{ pluralize('person', people.length, true) }}.</p>
</div>
<ChevronRightIcon class="h-5 w-5 text-gray-400 group-hover:text-black transition-all" />
</div>
<div class="group grid grid-cols-[auto_min-content] bg-gray-50 p-3 rounded-xl cursor-pointer shadow-sm hover:shadow hover:bg-white transition-all" @click="$router.push({ name: 'Vitals' })">
<div class="grid">
<h3 class="font-semibold mb-1">Vitals</h3>
<p class="text-gray-500 text-sm self-end">You are measuring {{ pluralize('vital', vitals.length, true) }}.</p>
</div>
<ChevronRightIcon class="h-5 w-5 text-gray-400 group-hover:text-black transition-all" />
</div>
<div class="group grid grid-cols-[auto_min-content] bg-gray-50 p-3 rounded-xl cursor-pointer shadow-sm hover:shadow hover:bg-white transition-all" @click="$router.push({ name: 'Measurements' })">
<div class="grid">
<h3 class="font-semibold mb-1">Measurements</h3>
<p class="text-gray-500 text-sm self-end">You have recorded {{ pluralize('measurement', measurements.length, true) }}.</p>
</div>
<ChevronRightIcon class="h-5 w-5 text-gray-400 group-hover:text-black transition-all" />
</hgroup>
</header>
<div class="m-4">
<h3 class="grid grid-flow-col text-xl font-bold mb-4 cursor-pointer" @click="$router.push({ name: 'People' })">
People
<span v-if="people.length > 2" class="font-normal text-base text-indigo-500 justify-self-end whitespace-nowrap">
View all {{ people.length }} <ChevronRightIcon class="w-6 h-6 inline align-top" />
</span>
</h3>
<div class="box-columns pb-10">
<PersonListItem v-for="person in people" :person="person" :key="person.id" />
</div>
</div>
</div>
Expand Down
63 changes: 63 additions & 0 deletions src/components/Interface/EditableContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<script setup>
import { ref } from 'vue';
import { CheckIcon, XMarkIcon } from '@heroicons/vue/24/outline';
import { PencilIcon } from '@heroicons/vue/20/solid';

const props = defineProps({
value: {
type: String,
required: true
},
type: {
type: String,
default: 'text'
},
placeholder: {
type: String,
default: null
},
inputClasses: {
type: String,
default: null
}
})

const emits = defineEmits(['updated']);

const newValue = ref(props.value);
const editMode = ref(false);
const input = ref();

const save = () => {
emits('updated', newValue.value);
editMode.value = false;
}

const activate = () => {
editMode.value = true;
setTimeout(() => {
input.value.focus();
}, 100)
}
</script>

<template>
<div>
<div v-if="editMode" class="group focus-within:ring-4 focus-within:ring-indigo-200 focus-within:border focus-within:border-indigo-500 border rounded grid grid-cols-[auto_min-content_min-content]">
<input class="!rounded-tr-none !rounded-br-none border-none !ring-0 leading-[24px]" name="editable-field" :class="inputClasses" ref="input" :type="props.type" :placeholder="props.placeholder" v-model="newValue" @keyup.enter="save" />
<button class="p-2 border-l text-indigo-500 bg-white hover:bg-indigo-50" @click="save"><CheckIcon class="w-5 h-5" /></button>
<button class="p-2 border-l rounded-tr rounded-br bg-white hover:bg-red-50 text-red-500" @click="editMode = false"><XMarkIcon class="w-5 h-5" /></button>
</div>
<div v-else class="relative grid grid-cols-[auto_min-content]">
<span
class="bg-gray-50 border border-gray-200 border-r-0 text-gray-500 leading-[24px] py-2 px-3 pr-10 rounded rounded-tr-none rounded-br-none truncate"
:class="[
{'text-gray-300 text-sm font-light' : placeholder && !props.value},
props.inputClasses
]">
{{ props.value || placeholder }}
</span>
<button class="border border-gray-200 text-indigo-500 rounded-tr rounded-br px-3 hover:bg-gray-50" @click="activate"><PencilIcon class="w-3.5 h-3.5" /></button>
</div>
</div>
</template>
91 changes: 36 additions & 55 deletions src/components/Layout.vue
Original file line number Diff line number Diff line change
@@ -1,104 +1,85 @@
<script setup>
import { SquaresPlusIcon, UsersIcon, Cog8ToothIcon, HomeIcon, ChartBarIcon, HeartIcon } from '@heroicons/vue/20/solid';
import { SquaresPlusIcon, Cog8ToothIcon, HomeIcon, ChartBarIcon, HeartIcon, UserIcon } from '@heroicons/vue/20/solid';
import { record } from '../store/record';
import { peers, webrtcConnected } from '../providers/webrtc';
import pluralize from 'pluralize';
import { store as preferencesStore } from '../store/preferences';
import { scrolled } from '../store/ui';
import { selectedPersonId } from '../store/person';

const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
window.addEventListener('scroll', () => {
scrolled.value = window.scrollY > 0;
});

</script>

<template>
<div v-if="record">
<div class="dash-layout grid grid-rows-[min-content_auto] grid-cols-1 p-4 py-1 h-screen md:max-h-screen md:min-h-screen md:gap-6 md:py-4 md:pr-0 md:grid-rows-none md:grid-flow-col md:grid-cols-[200px_auto]">
<div class="main-nav overflow-scroll md:overflow-visible items-center md:items-start z-20 justify-start md:justify-normal bg-gray-900 text-white p-4 py-2 rounded-3xl order-last fixed grid grid-flow-col self-start bottom-6 right-4 left-4 md:h-full md:sticky md:top-0 md:left-auto md:right-auto md:bottom-6 md:order-none md:rounded-xl md:py-6 gap-2 md:gap-4 md:grid-rows-[min-content_auto_min-content]">
<h2 class="grid grid-flow-col items-center md:block self-start font-bold text-xl md:mb-3">
<div class="dash-layout grid grid-rows-[auto_min-content] sm:grid-rows-none sm:grid-flow-col sm:grid-cols-[min-content_auto] min-h-screen sm:min-h-0">
<header
class="main-header sticky grid grid-flow-col sm:grid-rows-[min-content_auto_min-content] gap-2 sm:gap-4 overflow-scroll sm:overflow-visible sm:min-h-[calc(100vh-2rem)] items-center sm:items-start sm:justify-normal order-last sm:order-none self-end sm:self-start p-4 py-2 sm:py-6 bottom-6 md:bottom-auto mb-4 sm:mb-auto mr-4 sm:mr-0 ml-4 mt-4 top-4 rounded-3xl bg-gray-900 text-white z-20">
<h2 class="app-title grid grid-flow-col items-center sm:block self-start font-bold text-xl lg:mb-3 transition-all">
<RouterLink
class="grid grid-flow-col items-center md:justify-start gap-1 p-2 md:p-0"
class="grid grid-flow-col items-center sm:justify-start gap-1 sm:gap-4 p-2 sm:p-0 sm:px-3"
:to="{ name: 'Dashboard' }"
aria-label="Dashboard"
>
<SquaresPlusIcon class="h-6 w-6 md:h-5 md:w-5"/> <span class="hidden md:inline">HealthRecord</span>
<SquaresPlusIcon class="h-6 w-6"/> <span class="hidden lg:inline">HealthRecord</span>
</RouterLink>
</h2>
<nav @click="scrollToTop();">
<ul class="grid gap-2 md:gap-3 grid-flow-col md:grid-flow-row">
<nav class="main-nav">
<ul v-if="selectedPersonId" class="grid gap-2 sm:gap-4 grid-flow-col sm:grid-flow-row">
<li>
<RouterLink
class="grid grid-flow-col gap-3 p-2 px-3 text-sm justify-start items-center rounded-md text-gray-400 hover:bg-gray-800 hover:text-white"
:to="{ name: 'Dashboard' }"
active-class="bg-gray-700 text-white hover:bg-gray-700 active"
exact-active-class="bg-gray-700 text-white exact-active"
aria-label="Dashboard"
:to="{ name: 'Person', params: { personId: selectedPersonId } }"
aria-label="Person"
>
<HomeIcon class="h-6 w-6" /> <span class="hidden md:inline">Dashboard</span>
<UserIcon class="h-6 w-6" /> <span>Overview</span>
</RouterLink>
</li>
<li>
<RouterLink
class="grid grid-flow-col gap-3 p-2 px-3 text-sm justify-start items-center rounded-md text-gray-400 hover:bg-gray-800 hover:text-white"
:to="{ name: 'People' }"
active-class="bg-gray-700 text-white hover:bg-gray-700 active"
exact-active-class="bg-gray-700 text-white exact-active"
aria-label="People"
>
<UsersIcon class="h-6 w-6" /> <span class="hidden md:inline">People</span>
</RouterLink>
</li>
<li>
<RouterLink
class="grid grid-flow-col gap-3 p-2 px-3 text-sm justify-start items-center rounded-md text-gray-400 hover:bg-gray-800 hover:text-white"
:to="{ name: 'Vitals' }"
active-class="bg-gray-700 text-white hover:bg-gray-700 active"
exact-active-class="bg-gray-700 text-white exact-active"
:to="{ name: 'PersonVitals', params: { personId: selectedPersonId } }"
aria-label="Vitals"
>
<HeartIcon class="h-6 w-6" /> <span class="hidden md:inline">Vitals</span>
<HeartIcon class="h-6 w-6" /> <span>Vitals</span>
</RouterLink>
</li>
<li>
<RouterLink
class="grid grid-flow-col gap-3 p-2 px-3 text-sm justify-start items-center rounded-md text-gray-400 hover:bg-gray-800 hover:text-white"
:to="{ name: 'Measurements' }"
active-class="bg-gray-700 text-white hover:bg-gray-700 active"
exact-active-class="bg-gray-700 text-white exact-active"
:to="{ name: 'PersonMeasurements', params: { personId: selectedPersonId } }"
aria-label="Measurements"
>
<ChartBarIcon class="h-6 w-6" /> <span class="hidden md:inline">Measurements</span>
<ChartBarIcon class="h-6 w-6" /> <span>Measurements</span>
</RouterLink>
</li>
</ul>
</nav>
<nav class="">
<nav class="main-nav place-self-end sm:place-self-auto">
<ul>
<li>
<RouterLink
class="grid grid-flow-col gap-3 p-2 px-3 text-sm justify-start items-center rounded-md text-gray-400 hover:bg-gray-800 hover:text-white"
class="group"
:to="{ name: 'Settings' }"
active-class="bg-gray-700 text-white hover:bg-gray-700 active"
exact-active-class="bg-gray-700 text-white exact-active"
aria-label="Settings"
>
<span>
<span
v-if="preferencesStore.webRTC.enabled && webrtcConnected"
class="h-2 w-2 inline-block bg-orange-300 rounded-full absolute shadow"
:class="{ '!bg-green-500' : peers > 0 }"
:title="peers > 0 ? pluralize('peer', peers, true) + ' connected' : 'Waiting for peers...'"
></span>
<Cog8ToothIcon class="h-6 w-6 md:h-5 md:w-5" />
</span> <span class="hidden md:inline">Settings</span>
<span>
<span
v-if="record.user.preferences && record.user.preferences.webRTC.enabled && webrtcConnected"
class="h-3 w-3 inline-block bg-orange-300 rounded-full absolute border-2 border-gray-900 group-hover:border-gray-800"
:class="{ '!bg-green-500' : peers > 0 }"
:title="peers > 0 ? pluralize('peer', peers, true) + ' connected' : 'Waiting for peers...'"
></span>
<Cog8ToothIcon class="h-6 w-6" />
</span>
<span class="hidden md:inline">Settings</span>
</RouterLink>
</li>
</ul>
</nav>
</div>
<div class="grid grid-cols-1 md:overflow-y-scroll md:pr-4 md:pb-4 md:-mb-4">
</header>
<main>
<RouterView name="main" />
</div>
</main>
</div>
</div>
</template>
7 changes: 4 additions & 3 deletions src/components/Measurements/Form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { useRoute } from 'vue-router';

const route = useRoute();

const props = defineProps(['value', 'date', 'personId', 'vitalId']);
const emit = defineEmits(['submit']);
const props = defineProps(['value', 'date', 'personId', 'vitalId', 'deletable']);
const emit = defineEmits(['submit', 'delete']);

const value = ref(props.value || '');
const date = ref(dayjs(props.date).format('YYYY-MM-DD') || '');
Expand Down Expand Up @@ -65,8 +65,9 @@ const vital = computed(() => {
<label for="date">
Date
</label>
<input v-model="date" type="date" id="date" required>
<input v-model="date" type="datetime-local" id="date" required>
<div class="grid grid-flow-col justify-end items-center gap-5 mt-4">
<a v-if="deletable" @click="emit('delete')" class="text-sm text-red-500 font-light cursor-pointer">Delete</a>
<a @click="$router.back()" class="text-sm text-gray-500 font-light cursor-pointer">Cancel</a>
<button type="submit" class="btn" :disabled="!isFormComplete">Save</button>
</div>
Expand Down
Loading