Skip to content
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
3 changes: 3 additions & 0 deletions public/images/icons/icon-01.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/icons/icon-02.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/icons/icon-03.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions public/images/icons/icon-04.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/icons/icon-05.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/images/icons/icon-06.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/icons/icon-07.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/images/icons/icon-08.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
142 changes: 29 additions & 113 deletions src/pages/ProjectsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,27 @@
<section>
<!-- Page title -->
<h1 class="h1 font-aspekta mb-12">Nice stuff I've built</h1>

<!-- Page content -->
<div class="space-y-10">
<!-- Side Hustles cards -->
<section>
<h2 class="font-aspekta text-xl font-[650] mb-6">Side Hustles</h2>
<!-- Cards -->
<div class="grid sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2 gap-5">
<ProjectCardPartial v-for="item in items01" :key="item.id" :item="item" />
</div>
</section>
<!-- Client Projects cards -->
<section>
<h2 class="font-aspekta text-xl font-[650] mb-6">Client Projects</h2>
<!-- Cards -->
<div class="mb-5">
<p>
Over the years, I’ve built and shared command-line tools and frameworks to tackle real engineering challenges—complete
with clear docs and automated tests—and partnered with banks, insurers, and fintechs to deliver custom software that balances
performance, security, and scalability.
</p>
<p class="mt-2">
Feel free to dive into my open-source repos and client case studies to see how I turn
complex requirements into reliable, maintainable systems.
</p>
</div>
<section v-if="projects.length > 0">
<h2 class="font-aspekta text-xl font-[650] mb-6">Open Source / Client Projects</h2>
<div class="grid sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2 gap-5">
<ProjectCardPartial v-for="item in items02" :key="item.id" :item="item" />
<ProjectCardPartial v-for="project in projects" :key="project.uuid" :item="project" />
</div>
</section>

</div>
</section>
</div>
Expand All @@ -55,110 +58,23 @@
</div>
</template>

<script>
import { ref } from 'vue';

import SideNavPartial from '@partials/SideNavPartial.vue';
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useUserStore } from '@stores/users/user.ts';
import FooterPartial from '@partials/FooterPartial.vue';
import HeaderPartial from '@partials/HeaderPartial.vue';
import SideNavPartial from '@partials/SideNavPartial.vue';
import type { Project, User } from '@stores/users/userType.ts';
import ProjectCardPartial from '@partials/ProjectCardPartial.vue';
import WidgetNewsletterPartial from '@partials/WidgetNewsletterPartial.vue';
import WidgetSponsorPartial from '@partials/WidgetSponsorPartial.vue';
import FooterPartial from '@partials/FooterPartial.vue';

import Icon01 from '@images/project-icon-01.svg';
import Icon02 from '@images/project-icon-02.svg';
import Icon03 from '@images/project-icon-03.svg';
import Icon04 from '@images/project-icon-04.svg';
import Icon05 from '@images/project-icon-05.svg';
import Icon06 from '@images/project-icon-06.svg';
import Icon07 from '@images/project-icon-07.svg';
import Icon08 from '@images/project-icon-08.svg';

export default {
name: 'ProjectsPage',
components: {
SideNavPartial,
HeaderPartial,
ProjectCardPartial,
WidgetNewsletterPartial,
WidgetSponsorPartial,
FooterPartial,
},
setup() {
const items01 = ref([
{
id: 0,
icon: Icon01,
slug: '#0',
title: 'Container Tinkering',
excerpt: 'Solutions for running containers locally and remotely.',
openSource: true,
},
{
id: 0,
icon: Icon02,
slug: '#0',
title: 'Engine Prototypes',
excerpt: 'Solutions for running containers locally and remotely.',
openSource: false,
},
]);

const items02 = ref([
{
id: 0,
icon: Icon03,
slug: '#0',
title: 'PixelOkay',
excerpt: 'Code assets and services for people, with people.',
openSource: false,
},
{
id: 1,
icon: Icon04,
slug: '#0',
title: 'Storybook',
excerpt: 'Storybook helps you develop, test, and document UIs.',
openSource: false,
},
{
id: 2,
icon: Icon05,
slug: '#0',
title: 'Knowledge AI',
excerpt: 'Instantly answers all questions based on your internal knowledge bases.',
openSource: false,
},
{
id: 3,
icon: Icon06,
slug: '#0',
title: 'Security Frame',
excerpt: 'Automated security compliance for your business.',
openSource: false,
},
{
id: 4,
icon: Icon07,
slug: '#0',
title: 'KanbanOK',
excerpt: 'The most powerful kanban board ever invented.',
openSource: false,
},
{
id: 5,
icon: Icon08,
slug: '#0',
title: 'T Analytics',
excerpt: 'Make your Twitter analytics pretty and easy to share.',
openSource: false,
},
]);
const userStore = useUserStore();
const projects: Project[] = ref<Project[]>([]);

return {
items01,
items02,
};
},
};
onMounted(() => {
userStore.onBoot((profile: User) => {
projects.value = profile.projects;
});
});
</script>
35 changes: 20 additions & 15 deletions src/partials/ProjectCardPartial.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
<template>
<router-link
<a
class="rounded-lg border border-slate-200 hover:border-slate-300 dark:border-slate-800 dark:hover:border-slate-700 dark:bg-linear-to-t dark:from-slate-800 dark:to-slate-800/30 transition-color ease-in-out p-5 group"
:to="item.slug"
:href="item.url"
target="_blank"
rel="noopener noreferrer"
>
<div class="flex flex-col h-full">
<div class="grow">
<div class="flex items-center justify-between space-x-2">
<div class="h-10 w-10 flex items-center justify-center border border-slate-200 dark:border-slate-700 rounded-full mb-2">
<img :src="item.icon" width="18" height="18" :alt="item.title" />
<img
:src="image(item.icon)"
width="18"
height="18"
:alt="item.title"
/>
</div>
<div v-if="item.openSource" class="text-xs inline-flex items-center font-medium bg-green-100 text-green-600 rounded-full text-center px-2 h-5">Open-Source</div>
<div v-if="item.isOpenSource" class="text-xs inline-flex items-center font-medium bg-green-100 text-green-600 rounded-full text-center px-2 h-5">Open-Source</div>
</div>
<div class="text-lg font-aspekta font-[650] mb-1">{{ item.title }}</div>
<p class="text-sm text-slate-500 dark:text-slate-400 mb-2">{{ item.excerpt }}</p>
Expand All @@ -20,17 +27,15 @@
</svg>
</div>
</div>
</router-link>
</a>
</template>

<script>
export default {
name: 'ProjectCardPartial',
props: {
item: {
type: Object,
required: true,
},
},
};
<script setup lang="ts">
import { image } from '@/public.ts';
import type { Project } from '@stores/users/userType.ts';

const { item } = defineProps<{
item: Project;
}>()

</script>
22 changes: 21 additions & 1 deletion src/stores/users/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,19 @@ export const Response: User = {
title: 'Think of your energy as an invisible compass.',
excerpt: 'After experiencing the highs and lows of going into business with a family member, I reached a significant turning point in my life.',
url: 'https://github.com/aurachakra',
isOpenSource: false,
icon: "icons/icon-01.svg",
created_at: '2023-02-25',
updated_at: '2023-10-05',
},
{
uuid: '00a0a12e-6af0-4f5a-b96d-3c95cc7c365c',
uuid: '2d178e11-a584-4e20-a493-3b84007dd358',
language: 'Vue / TypeScript',
title: "Gus's personal website.",
excerpt: 'Gus is a full-stack Software Engineer who has been building web technologies for more two decades.',
url: 'https://github.com/gocantodev/client',
isOpenSource: true,
icon: "icons/icon-02.svg",
created_at: '2021-11-03',
updated_at: '2024-09-29',
},
Expand All @@ -164,6 +168,8 @@ export const Response: User = {
title: 'users-grpc-service',
excerpt: 'users server & client communications service.',
url: 'https://github.com/gocanto/users-grpc-service',
isOpenSource: true,
icon: "icons/icon-03.svg",
created_at: '2022-04-17',
updated_at: '2025-04-22',
},
Expand All @@ -173,6 +179,8 @@ export const Response: User = {
title: 'laravel-simple-pdf',
excerpt: 'Simple laravel PDF generator.',
url: 'https://github.com/gocanto/laravel-simple-pdf',
isOpenSource: true,
icon: "icons/icon-04.svg",
created_at: '2019-06-11',
updated_at: '2020-12-26',
},
Expand All @@ -182,6 +190,8 @@ export const Response: User = {
title: 'vuemit',
excerpt: 'The smallest Vue.js events handler.',
url: 'https://github.com/gocanto/vuemit',
isOpenSource: true,
icon: "icons/icon-05.svg",
created_at: '2017-02-01',
updated_at: '2021-08-11',
},
Expand All @@ -191,6 +201,8 @@ export const Response: User = {
title: 'google-autocomplete',
excerpt: 'Google Autocomplete Vue Component.',
url: 'https://github.com/gocanto/google-autocomplete',
isOpenSource: true,
icon: "icons/icon-06.svg",
created_at: '2016-07-02',
updated_at: '2021-08-11',
},
Expand All @@ -200,6 +212,8 @@ export const Response: User = {
title: 'converter-go',
excerpt: "Currency converter that's data-agnostic.",
url: 'https://github.com/gocanto/go-converter',
isOpenSource: true,
icon: "icons/icon-07.svg",
created_at: '2021-09-02',
updated_at: '2021-10-11',
},
Expand All @@ -209,6 +223,8 @@ export const Response: User = {
title: 'http-client',
excerpt: 'Http client that handles retries, logging & dynamic headers.',
url: 'https://github.com/gocanto/http-client',
isOpenSource: true,
icon: "icons/icon-08.svg",
created_at: '2019-07-01',
updated_at: '2022-12-22',
},
Expand All @@ -218,6 +234,8 @@ export const Response: User = {
title: 'converter',
excerpt: "Immutable PHP currency converter that's data-agnostic.",
url: 'https://github.com/gocanto/converter',
isOpenSource: true,
icon: "icons/icon-01.svg",
created_at: '2019-06-07',
updated_at: '2019-06-11',
},
Expand All @@ -227,6 +245,8 @@ export const Response: User = {
title: 'Laravel Framework',
excerpt: 'Contributions to the Laravel Framework.',
url: 'https://github.com/laravel/framework/pulls?q=is%3Apr+is%3Aclosed+author%3Agocanto',
isOpenSource: true,
icon: "icons/icon-02.svg",
created_at: '2017-07-06',
updated_at: '2022-09-15',
},
Expand Down
2 changes: 2 additions & 0 deletions src/stores/users/userType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export interface Project {
excerpt: string;
description?: string;
url: string;
isOpenSource: boolean;
icon: string;
created_at: string;
updated_at: string;
}
Expand Down