bastislack / highline-freestyle

Webapp for Highline Freestyle
GNU General Public License v3.0
10 stars 9 forks source link

Migrate to Vite #245

Closed WaldemarLehner closed 12 months ago

WaldemarLehner commented 1 year ago

With the presence of #242 I would also like to mention Vite. Vite acts as a replacement for Webpack – it is responsible for the dev server, as well as bundling the result.

Looking at the webpack config I don't see anything that is directly dependent on Webpack.

Vite has support for scss/sass and brings many things like loading images out of the box. There is also plugins for other things. For example, adding PWA support is as easy as adding one plugin.

I think that Vite would be a better choice as it brings quicker build times, but also a better developer experience.

Some other things that could be beneficial with Vite would be adding Unit Tests with Vitest and also loaders for csv values. As of now, tricks are currently modified manually in a JS file. One could instead write a plugin that fetches the data from google sheets during compile time and provide mocking data during dev mode.

weberax commented 1 year ago

Sounds good!

bastislack commented 1 year ago

@WaldemarLehner Would you be motivated to make the initial setup with TypeScript and Vite?

WaldemarLehner commented 1 year ago

@bastislack I am already on it :eyes: https://github.com/WaldemarLehner/highline-freestyle/tree/try-vite-ts-rewrite :D

My approach was to start with the Vite scaffold and copy stuff over, add dependencies as needed.

Currently not in a working state, but I will create a PR once it is.

WaldemarLehner commented 1 year ago

I've got a question in regards to types.

Currently, I have set up the following schema for a trick:

import {z} from "zod";
import {ZodPositionEnum, ZodStickFrequencyEnum} from "./enums";

/**
 * Type-safe Trick Object Definition with zod.
 */
const ZodTrick = z.object({
  id: z.number().int(),
  alias: z.string().optional(),
  technicalName: z.string().optional(),
  establishedBy: z.string().optional(),
  yearEstablished: z.number().int().positive(),
  linkToVideo: z.string().url().optional(),
  startPos: ZodPositionEnum,
  endPos: ZodPositionEnum,
  difficultyLevel: z.number().int().min(0).max(11),
  description: z.string(),
  tips: z.array(z.string()).optional(),
  stickFrequency: ZodStickFrequencyEnum.optional(),
  recommendedPrerequisiteIds: z.array(z.number()).optional(),
});

export type Trick = z.infer<typeof ZodTrick>;

I wrote a Vite Plugin to fetch data from the sheet at compile time and serve it as a virtual module. You can see the result below :) image

The schema fails however because some entries are missing the year and difficulty level.

This means we need to either: make Year / DifficultyLevel optional, or add the values to the Sheet.

I guess the most sensible approach is to make Year optional, but add difficulty levels.

Here is the JSON containing all Schema errors (collapsed):

!!Click to expand!! ```json [ { "offendingEntryId": 10000, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10003, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10006, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10007, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10011, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10015, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10017, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10018, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10019, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10020, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10021, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10022, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10042, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10046, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10047, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10048, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10050, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10051, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10070, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10071, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10072, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10073, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10074, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10086, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10090, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10101, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10102, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10103, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10104, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10105, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10122, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10123, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10156, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10179, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10180, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10181, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10182, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10183, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10184, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10185, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10186, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10187, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10188, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10189, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10190, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10191, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10192, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10199, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10200, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10205, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10206, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10213, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10214, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10215, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10222, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10224, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10225, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10226, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10227, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10228, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10229, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10230, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10231, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10236, "reason": "Bad Description", "message": "Description cannot be empty", "value": "" }, { "offendingEntryId": 10238, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10239, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10240, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10241, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10242, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10243, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10244, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10245, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10246, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10247, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10248, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10249, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10250, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10251, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10252, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10253, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10254, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10255, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10256, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10257, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10258, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10259, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10260, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10261, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10262, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10263, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10264, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10265, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10266, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10267, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10268, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10269, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10270, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10271, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10272, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10273, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10274, "reason": "Bad Difficulty Level", "message": "Difficulty Level cannot be parsed as an Integer", "value": "" }, { "offendingEntryId": 10275, "reason": "Bad Year Established", "message": "Year Established cannot be parsed as an Integer", "value": "" } ] ```
bastislack commented 1 year ago

Nice work! :)

As you said the difficultyLevel is required, I am just still working on the trick-list and haven't finished all the entries yet.

For the year we could make it optional, or I could just write 'unknown' or '?' for the tricks that I don't know the establishedYear. I think providing '?' might be helpful for the sorting of the trick list as then there will be headers for each year and one header with '?'. Should I add this in the google sheet?

WaldemarLehner commented 1 year ago

The Question mark could be added in the UI. Leave it blank and I ill make it optional. UI can check for a value and fall back to the question mark

WaldemarLehner commented 1 year ago

While I am working on the Data I would also like to mention the Database. Currently, user and predefined Tricks are stored there. What is the reason behind predefined tricks being stored there? I would just access the virtual module instead to get them

bastislack commented 1 year ago

We wanted to give the user the possibility to overwrite the predefined tricks, e.g. if they do not agree with the difficultyLevel or the name, that's why we included them in the db.

WaldemarLehner commented 1 year ago

I see. What about allowing the user to create a user-defined one with the same ID. The DB looks up the user-defined stuff first and then falls back the predefined data?

bastislack commented 1 year ago

Sounds good to me! One thing we need to think about is changes that are made to already existing predefined tricks. E.g. We decide to downgrade a trick or update the video to the trick. The user would then not see the update because he already has a user-defined trick that 'overwrites' the predefined trick.

WaldemarLehner commented 1 year ago

What about a migration that does a diff with the spreadsheet data and — if differences were found — creates a new entry in a overrides-Table.

bastislack commented 1 year ago

So then the trick would exist twice (one user-adjusted trick and the update of the predefined trick) ? I guess one thing we could do is to see whether the user introduced differences in a field that also got updated by a new version of the predefined trick. If so, we can prompt the user to select which version (the user's version or the updated version of the predefined trick) he wants to keep. If there are only user changes made to fields that didn't change in the update of the predefined trick we just merge those two versions. I guess, just like merging two branches in git, we only have a problem, if there is a merge conflict :)

weberax commented 1 year ago

In #239 we are thinking of merging the two tables... I tried to outline here the needed steps: https://github.com/bastislack/highline-freestyle/issues/239#issuecomment-1469027255

WaldemarLehner commented 1 year ago

@bastislack My suggestion is a bit flawed in that I had the assumption that predefined Tricks are entirely read-only, as in — no notes, no "boosted", etc.

@JulianDietzel and myself discussed what could be a sensible DB Structure to allow for predefined and user-defined tricks. We ill try to finalize the suggestion in the coming days and add it to #239 as a comment.

bastislack commented 1 year ago

You guys rock!! Thank you both for your work, that is amazing! :)

WaldemarLehner commented 12 months ago

Closing issue as it's being implicitly resolved by the complete rewrite