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

Update status #25

Merged
merged 9 commits into from
Sep 25, 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
73 changes: 6 additions & 67 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,80 +1,19 @@
<script setup lang="ts">
import { invoke } from '@tauri-apps/api/tauri';
import { onMounted, reactive } from 'vue';
import { toBackendFarmObject, BackendFarmObject, FarmListItem } from './farms';
import { GREEN, RED, YELLOW, toStatusObject } from './status';
import FarmList from './components/FarmList.vue';

const farms: FarmListItem[] = reactive([]);

function save() {
const cache: BackendFarmObject[] = farms.map(toBackendFarmObject);
invoke('save', { farms: cache });
}

onMounted(() => {
invoke('load').then((cache: BackendFarmObject[]): void => {
const list: FarmListItem[] = cache.map((f) => ({
...f, status: toStatusObject(f.status),
}));
farms.push(...list);
});
})

const examples: FarmListItem[] = [
{
id: 0,
name: 'Joe\'s Farm',
status: toStatusObject(RED),
timestamp: 20
},
{
id: 1,
name: 'Sally\'s Farm',
status: toStatusObject(YELLOW),
timestamp: 5
},
{
id: 2,
name: 'Sam\'s Farm',
status: toStatusObject(GREEN),
timestamp: 0
},
];
import ProductionStatusByFarm from './views/ProductionStatusByFarm.vue';

</script>

<template>
<header>
<hgroup>
<h1>Richland Gro-Op: Spring Crop Plan</h1>
<span role="button" @click="save" class="outline">Save</span>
&nbsp;
<span role="button"
v-if="farms.length === 0"
@click="farms.push(...examples)"
class="outline secondary">
Examples
</span>
&nbsp;
<span
role="button"
v-if="farms.length > 0"
@click="farms.splice(0)"
class="outline secondary">
Clear
</span>
</hgroup>
</header>
<main class="container">
<div class="list-container">
<farm-list :farms="farms"></farm-list>
</div>
<header>
<h1>Richland Gro-Op: Spring Crop Plan</h1>
</header>
<production-status-by-farm/>
</main>
</template>

<style scoped>
header {
main header {
padding-top: 10vh;
text-align: center;
}
Expand Down
47 changes: 0 additions & 47 deletions src/components/FarmList.vue

This file was deleted.

37 changes: 37 additions & 0 deletions src/components/Modal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script setup lang="ts">
export interface ModalProps {
closeBtn?: boolean
}
const { closeBtn } = withDefaults(defineProps<ModalProps>(), {
closeBtn: true,
});

defineEmits<{
close: [event: EventTarget]
}>();

defineSlots<{
default(): any
header(): any
footer(): any
}>();
</script>

<template>
<dialog open>
<article>
<header v-if="typeof $slots.header === 'function'">
<a v-if="closeBtn" aria-label="Close" class="close" @click="$emit('close')"></a>
<slot name="header"></slot>
</header>
<slot></slot>
<footer v-if="(typeof $slots.footer === 'function')">
<slot name="footer"></slot>
</footer>
</article>
</dialog>
</template>

<style scoped>

</style>
37 changes: 37 additions & 0 deletions src/components/RadioInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script setup lang="ts">
import { computed } from 'vue';

const {
value, checked, label
} = defineProps<{
value: any,
checked: boolean,
label: string,
}>();
console.log('labelText', label);
const id = computed(() => label.toLowerCase());

const emit = defineEmits<{
input: [id: string, value: any]
}>()
</script>

<template>
<span class="input-group">
<input
type="radio"
:id="id"
:name="id"
:value="value"
:checked="checked"
@input="emit('input', id, value)">
<label :for="id">{{ label }}</label>
</span>
</template>

<style scoped>
.input-group {
display: flex;
flex-flow: row;
}
</style>
51 changes: 49 additions & 2 deletions src/farms.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { StatusObject } from "./status";
import { invoke } from '@tauri-apps/api/tauri';
import { onMounted, reactive } from 'vue';
import { StatusObject, toStatusObject, RED, YELLOW, GREEN } from './status';

export interface BackendFarmObject {
id: number,
Expand All @@ -13,5 +15,50 @@ export interface FarmListItem {
timestamp: number,
}

export const toBackendFarmObject = (f: FarmListItem): BackendFarmObject =>
const toBackendFarmObject = (f: FarmListItem): BackendFarmObject =>
({ ...f, status: f.status.title });

export default function useFarms() {
const farms: FarmListItem[] = reactive([]);
onMounted(() => {
invoke('load').then((cache: BackendFarmObject[]): void => {
const list: FarmListItem[] = cache.map((f) => ({
...f, status: toStatusObject(f.status),
}));
farms.push(...list);
});
})

function save() {
const cache: BackendFarmObject[] = farms.map(toBackendFarmObject);
invoke('save', { farms: cache });
}

function setFarmStatus(i: number, color: string|symbol): void {
farms[i].status = toStatusObject(color);
}
return {
farms, save, setFarmStatus,
}
}

export const examples: FarmListItem[] = [
{
id: 0,
name: 'Joe\'s Farm',
status: toStatusObject(RED),
timestamp: 20
},
{
id: 1,
name: 'Sally\'s Farm',
status: toStatusObject(YELLOW),
timestamp: 5
},
{
id: 2,
name: 'Sam\'s Farm',
status: toStatusObject(GREEN),
timestamp: 0
},
];
4 changes: 3 additions & 1 deletion src/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface StatusObject {
interface ColorMap {
[color: symbol]: { title: string, emoji: string }
}
const colorMap: ColorMap = {
export const colorMap: ColorMap = {
[RED]: {
emoji: '🔴',
title: 'Red',
Expand All @@ -26,6 +26,8 @@ const colorMap: ColorMap = {
title: 'Green',
},
};
export const colorList = Object.getOwnPropertySymbols(colorMap)
.map(symbol => ({ ...colorMap[symbol], symbol }));

const fromSymbol = (symbol: symbol): StatusObject => {
if (symbol in colorMap) return {
Expand Down
101 changes: 101 additions & 0 deletions src/views/ProductionStatusByFarm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
import { ref, Teleport } from 'vue';
import useFarms, { examples } from '../farms';
import { colorList } from '../status';
import Modal from '../components/Modal.vue';
import RadioInput from '../components/RadioInput.vue';

const {
farms, save, setFarmStatus,
} = useFarms();

const selectedFarmIndex = ref(-1);
function editFarm(i) {
selectedFarmIndex.value = i;
}
</script>

<template>
<teleport to="body">
<modal v-if="selectedFarmIndex >= 0" @close="editFarm(-1)">
<template #header>
Edit {{ farms[selectedFarmIndex]?.name }} Status
</template>
<fieldset>
<radio-input
v-for="(c, i) in colorList"
:label="c.title"
:value="i"
:checked="c.symbol === farms[selectedFarmIndex]?.status?.symbol"
@input="setFarmStatus(selectedFarmIndex, c.symbol)"
:key="`color-radio-${i}`"/>
</fieldset>
</modal>
</teleport>
<section>
<fieldset class="save-btn-group">
<span role="button" @click="save" class="outline">Save</span>
&nbsp;
<span role="button"
v-if="farms.length === 0"
@click="farms.push(...examples)"
class="outline secondary">
Examples
</span>
&nbsp;
<span
role="button"
v-if="farms.length > 0"
@click="farms.splice(0)"
class="outline secondary">
Clear
</span>
</fieldset>
<table role="grid">
<thead>
<tr>
<th colspan="3">Production Status by Farm</th>
</tr>
</thead>
<tbody>
<tr
v-for="(farm, i) in farms" :key="`farm-${i}`"
@click="editFarm(i)">
<td class="status">
<span>
{{ farm.status.emoji }}
</span>
</td>
<td class="name">{{ farm.name }}</td>
<td class="secondary-info">
Last report: {{
farm.timestamp === 0 ? 'Today' : `${farm.timestamp} days ago`
}}
</td>
</tr>
</tbody>
</table>
</section>
</template>

<style scoped>
fieldset.save-btn-group {
text-align: center;
}
th[colspan="3"] {
text-align: center;
font-size: 1.75rem;
}

.secondary-info {
color: #777;
}

.status {
font-size: 2rem;
}

.name {
font-size: 1.5rem;
}
</style>