Skip to content

Commit

Permalink
BREAKING CHANGE: encapsulate restrictions in puzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
George-Spanos committed Aug 7, 2024
1 parent ad0f248 commit 3316c1f
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 64 deletions.
4 changes: 2 additions & 2 deletions app/components/Calendar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const { data: puzzles, status } = await useAsyncData(
async () => {
const { data: puzzles } = await supabase
.from("puzzle")
.select("puzzle_name, date")
.select("name, date")
.gte("date", getFirstDayOfMonth(month.value, year.value))
.lte("date", getLastDayOfMonth(month.value, year.value));
Expand All @@ -19,7 +19,7 @@ const { data: puzzles, status } = await useAsyncData(
function getPuzzleNameByDay(day: number): string | undefined {
if (!puzzles.value?.length) return;
const date = formatDate(day, month.value, year.value);
return puzzles.value.find((p) => p.date === date)?.puzzle_name;
return puzzles.value.find((p) => p.date === date)?.name;
}
</script>
Expand Down
6 changes: 2 additions & 4 deletions app/components/EditRestriction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ function deleteChampion(championName: string) {
async function saveRestriction() {
const restriction: Partial<Restriction> = {
name: transformDisplayName(name.value),
display_name: name.value,
name: name.value,
hash: uuid(),
champion_list: selectedChampions.value
};
Expand All @@ -43,7 +42,6 @@ async function saveRestriction() {
method: "POST",
body: restriction,
});
console.log(res);
toast.add({ title: 'Restriction saved.' });
navigateTo('/manage-restrictions');
}
Expand All @@ -55,7 +53,7 @@ async function saveRestriction() {
<h2 class="text-xl text-center">{{ restriction ? restriction.name : 'Create new restriction' }}</h2>
<UInput placeholder="Name... " size="lg" v-model="name" />
<UInputMenu size="lg" v-model:query="query" v-model="selectedChampion" placeholder="Start typing to add a champion"
searchable :options value-attribute="id" option-attribute="name" :popper="{}" />
searchable :options value-attribute="id" option-attribute="name" />
<UButton :disabled="!selectedChampions.length" class="self-center" @click="saveRestriction">save</UButton>

<UDivider />
Expand Down
35 changes: 21 additions & 14 deletions app/components/Puzzle.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script setup lang="ts">
import { Restriction as RestrictionT, type Puzzle } from "#imports";
import * as v from "valibot";
const toast = useToast();
import { calculateResults } from "~/utils/results";
const props = defineProps<{
puzzle: Puzzle;
Expand All @@ -12,33 +11,40 @@ const emits = defineEmits<{
save: [puzzle: Puzzle];
}>();
const toast = useToast();
const restrictions = inject<Ref<RestrictionT[]>>("restrictions");
if (!restrictions) throw createError("restrictios not found");
const form = reactive({
name: props.puzzle.puzzle_name,
colRestrictions: props.puzzle.col_restrictions,
rowRestrictions: props.puzzle.row_restrictions,
name: props.puzzle.name,
colRestrictions: props.puzzle.col_restrictions.map(cr => cr.id),
rowRestrictions: props.puzzle.row_restrictions.map(cr => cr.id),
});
const FormSchema = v.object({
const FormSchema = v.pipe(v.object({
name: v.pipe(v.string(), v.minLength(5)),
colRestrictions: v.pipe(v.array(v.string()), v.length(3)),
rowRestrictions: v.pipe(v.array(v.string()), v.length(3)),
});
}), v.transform((v) => ({
name: v.name,
col_restrictions: v.colRestrictions.map(cr => restrictions.value.find(r => r.id === cr)) as RestrictionT[],
row_restrictions: v.rowRestrictions.map(cr => restrictions.value.find(r => r.id === cr)) as RestrictionT[],
})));
const submitDisabled = computed(() => {
if (results.value.some(v => v === 0)) return false;
if (results.value.some(v => v === 0)) return true;
const { success } = v.safeParse(FormSchema, form);
return !success;
});
const restrictions = inject<Ref<RestrictionT[]>>("restrictions");
if (!restrictions) throw createError("restrictios not found");
const results = computed(() => {
const results = new Array(9).fill(0);
form.rowRestrictions.forEach((rr, idx) => {
form.colRestrictions.forEach((cr, idy) => {
if (rr && cr)
results[idy + idx * 3] = calculateResults(restrictions.value, rr, cr);
results[idy + idx * 3] = calculateResults(restrictions.value, cr, rr);
});
});
return results;
Expand All @@ -51,10 +57,11 @@ async function save() {
const body: Puzzle = {
id: props.puzzle.id,
date: props.puzzle.date,
row_restrictions: output.rowRestrictions,
col_restrictions: output.colRestrictions,
puzzle_name: output.name,
row_restrictions: output.row_restrictions,
col_restrictions: output.col_restrictions,
name: output.name,
};
console.log(body);
await $fetch("/api/puzzle", {
method: "POST",
body,
Expand Down
4 changes: 2 additions & 2 deletions app/components/Restriction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ const restrictions = inject<Ref<Restriction[]>>("restrictions");
</script>

<template>
<USelectMenu size="lg" :searchable="true" v-model="selected" :options="restrictions" option-attribute="display_name" value-attribute="id"
:search-attributes="['display_name', 'name']" />
<USelectMenu size="lg" :searchable="true" v-model="selected" :options="restrictions" option-attribute="name" value-attribute="id"
:search-attributes="['name', 'name']" />
</template>
2 changes: 1 addition & 1 deletion app/pages/manage-restrictions/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const supabase = useSupabaseClient();
const { data: restrictions } = await fetchRestrictions(supabase);
provide('restrictions', restrictions);
const columns = [{
key: 'display_name',
key: 'name',
label: 'Name',
sortable: true
},
Expand Down
15 changes: 8 additions & 7 deletions app/utils/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const filterByTwoOccurencies = <T>(arr: T[]) => filterByOccurencies(arr, 2);
const filterByOccurencies = <T>(arr: T[], occurencies: number): T[] =>
arr.filter((v) => countOccurrencies(arr)(v) >= occurencies);


export function calculateResults(
restrictions: Restriction[],
rowRestrictionId: string,
Expand All @@ -22,9 +23,12 @@ export function calculateResults(
if (!colRestriction)
throw createError(
`there is a problem with row restriction with id ${colRestriction}. Restrictions is part of a puzzle and does not exist in the database.`);
const arr = rowRestriction?.champion_list.concat(
colRestriction.champion_list,
);

return getRestrictionPossibleAnswers(rowRestriction, colRestriction);
}

export function getRestrictionPossibleAnswers(a: Restriction, b: Restriction): string[] {
const arr = a?.champion_list.concat(b.champion_list);
return Array.from(new Set(filterByTwoOccurencies(arr)));
}

Expand All @@ -33,10 +37,7 @@ export function filterRestrictions(term: string, restrictions: Restriction[]): R
term = term.toLowerCase();
return restrictions.filter(r =>
r.name.toLowerCase().startsWith(term)
|| r.display_name.toLowerCase().startsWith(term)
|| r.name.toLowerCase().startsWith(term)
|| r.champion_list.map(c => c.toLowerCase()).join(', ').includes(term)
);
}
export function transformDisplayName(name: string): string {
return name.toLowerCase().replace(' ', '_').replace('>=', '_gte_').replace('<=', '_lte_').replace('>', '_gt_').replace('<', '_lt_').replace('=', '_eq_');
}
35 changes: 18 additions & 17 deletions app/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
import * as v from "valibot";

export const Restriction = v.object({
id: v.string(),
hash: v.string(),
champion_list: v.array(v.string()),
name: v.string(),
created_at: v.string(),
});

export type Restriction = v.InferOutput<typeof Restriction>;

export const Puzzle = v.object({
id: v.optional(v.string()),
puzzle_name: v.string(),
row_restrictions: v.array(v.string()),
col_restrictions: v.array(v.string()),
name: v.string(),
row_restrictions: v.pipe(v.array(Restriction), v.length(3)),
col_restrictions: v.pipe(v.array(Restriction), v.length(3)),
date: v.string(),
});
export type Puzzle = v.InferOutput<typeof Puzzle>;

export function createInitialPuzzle(date: string): Puzzle {
return { col_restrictions: [], row_restrictions: [], date, puzzle_name: "" };
return { col_restrictions: [], row_restrictions: [], date, name: "" };
}

export const Restriction = v.object({
id: v.string(),
name: v.string(),
hash: v.string(),
champion_list: v.array(v.string()),
display_name: v.string(),
created_at: v.string(),
});

export type Restriction = v.InferOutput<typeof Restriction>;

export const Champion = v.pipe(
v.object({
name: v.string(),
champion_id: v.string(),
name: v.string(),
champion_id: v.string(),
}),
v.transform((c) => ({
name: c.name,
id: c.champion_id,
name: c.name,
id: c.champion_id,
}))
);

Expand Down
2 changes: 1 addition & 1 deletion server/api/puzzle.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default defineEventHandler(async (event) => {
console.error(issues.toString());
return "failed to validate puzzle schema";
}
const { data, error } = await supabase
const { error } = await supabase
.from("puzzle")
.upsert(output as any)
.select();
Expand Down
2 changes: 1 addition & 1 deletion server/api/restriction.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default defineEventHandler(async event => {
setResponseStatus(event, 400, 'failed to parse restriction');
return;
}
const { data, error } = await supabase.from('restriction').upsert(restriction as any).select();
const { error } = await supabase.from('restriction').upsert(restriction as any).select();

if (error) {
console.error(error);
Expand Down
27 changes: 14 additions & 13 deletions server/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import * as v from "valibot";

export const Restriction = v.object({
name: v.string(),
champion_list: v.array(v.string()),
created_at: v.optional(v.string()),

id: v.optional(v.string()),
hash: v.optional(v.string()),
});

export type Restriction = v.InferOutput<typeof Restriction>;
export const Puzzle = v.object({
id: v.optional(v.string()),
puzzle_name: v.string(),
row_restrictions: v.array(v.string()),
col_restrictions: v.array(v.string()),
name: v.string(),
row_restrictions: v.pipe(v.array(Restriction), v.length(3)),
col_restrictions: v.pipe(v.array(Restriction), v.length(3)),
date: v.string(),
});
export type Puzzle = v.InferOutput<typeof Puzzle>;

export function createInitialPuzzle(date: string): Puzzle {
return { col_restrictions: [], row_restrictions: [], date, puzzle_name: "" };
return { col_restrictions: [], row_restrictions: [], date, name: "" };
}

export const Restriction = v.object({
id: v.optional(v.string()),
name: v.string(),
hash: v.string(),
champion_list: v.array(v.string()),
display_name: v.string(),
created_at: v.optional(v.string()),
});

export type Restriction = v.InferOutput<typeof Restriction>;
2 changes: 0 additions & 2 deletions todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@

- Convert calendar route check to middleware
- encapsulate $fetch in use async data
- save restrictions champion list as id array instead of name array

0 comments on commit 3316c1f

Please sign in to comment.