A continuation of the story about the development of a pet project about cafés and co-working spaces in sunny Cyprus. Workplaces for digital nomads ヽ(。_°)ノ
I talked the API microservice in the first part, the frontend site in the second part.
The project code is open, website https://workplaces.cy/.
Architecture
The site is built as a PWA using the Vue 3 Composition API and the Vuetify UI framework. Both tools are ideal for a quick start from scratch and contain less redundant code than earlier versions.
The major portion of the site is Google Maps in its always-free tier, which displays data from the microservice's REST API, as well as a panel with filters and a list of workplaces.
The entry component src/components/Home.vue:
- manages the display of sections for mobile and desktop versions;
- manages data from the API, which is retrieved from src/composable/api.js;
- handles them with computed filters from src/components/Filters.vue;
- passes the filtered data to component src/components/Places.vue to display a list of locations;
- handles a 404 error;
- sets meta tags via component src/components/PlaceHead.vue.
There is no separate "place card" in the project, a simple scrolling of the place list to the required place is sufficient for now:
watch(
() => props.selectedPlaceId,
id => {
scrollToPlace(id)
}
)
The filters are integrated in src/components/Filters.vue and stylized further for a more compact appearance.
Meta tags are handled by the vueuse/head package.
The fawmi/vue-google-maps library, version 0.9.72, is used to display the map. The author broke the map in later versions and did not publish the source code on GitHub (however, these versions are available via npm).
You may also see local points of interest by displaying your location on a map.
Swiper/vue and a number of components are used to display large and small photographs of locations in a carousel.
In the HSL colour space, a gradient colour rating of places is calculated using a simple function in src/composable/colors.js. Only the hue changes depending on the rating value, while the saturation and brightness remain fixed.
On the place cards, there is a Complain button to report any inaccuracies - it's all serious:-)
A simple src/router/index.js router based on vue-router allows you to avoid utilising the store for now (with the current project capabilities) and helps with 404 error handling.
Most components are loaded asynchronously to speed up page display, for example, const Navigation = defineAsyncComponent() => import("/Navigation.vue"))
.
When opening/closing the panel, a portion of the template is wrapped into <KeepAlive> to cache components. However, it did not work with Suspense for displaying stubs :-(
Instead of using Vuetify's default font icons, the project imports SVG icons from the mdi/js package at https://materialdesignicons.com. This saves approximately 1 MB in the final package.
Builds the project with Vite and some magic in vite.config.js to optimise the final code, including CSS and HTML minification and the creation of a lighter version of Sentry.
PWA is created with vite-pwa/vite-plugin-pwa, and its parameters are set in vite.config.js. In summary, the implementation of PWA was not a project aim, but it did provide good caching of all project components and resulted in a very quick reopening of the site.
About Vuetify
Overall, I preferred Vuetify over Quazar and other UI frameworks. However, it has its own drawbacks:
- The difficulty in finely customising visual components. Vuetify is built for Material Design, therefore you can't just remove the indents between checkboxes or make mobile buttons smaller. Because of the high DOM depth, you may need to use :deep or wrap components in additional divs in some circumstances, lowering your Google PageSpeed score.
- Unable to get rid of unused styles. Importing components individually and setting styles in SASS can greatly simplify builds, but several common unused styles can't be deleted using PurgeCSS and analogues due to dynamic class names.
- Some components, such as the slider, have few features.
But I'm sure, this framework is good enough for many projects.
Deployment
The same Fly.io platform with managed micro VM Firecracker. It's much easier than hosting the microservice API here, and a four-line Dockerfile suffices:
FROM pierrezemb/gostatic
COPY docker/config/headerConfig.json /config/
COPY dist/ /srv/http/
ENTRYPOINT ["/goStatic", "-fallback", "/index.html"]
CI/CD
This is slightly more complicated: first, Github Action creates a build with Vite, then flyctl
creates a container from it and deploys it to the production VM. All secrets are stored in the GitHub production environment in this case.
Monitoring
The same Sentry and Honeybadger.
At this stage, the website is live, hosted in a production and available to all users. The project is fully completed :-)
Top comments (0)