fix: add blog pages and fix project schema, remove internal TimeNet CLI
CI/CD Pipeline / Build & Deploy (push) Successful in 23s
CI/CD Pipeline / Build & Deploy (push) Successful in 23s
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
---
|
||||
title: "Python Project Template"
|
||||
description: "Opinionated Python project template with modern tooling: uv, ruff, mypy, pytest, Docker, and CI/CD ready"
|
||||
pubDate: 2026-05-01
|
||||
url: "https://gitlab.impresion3d.pro/root/python-project-template"
|
||||
status: "active"
|
||||
stack: ["Python", "Docker", "Gitea Actions", "Portainer"]
|
||||
tags: ["Python", "Docker", "CI/CD", "DevOps"]
|
||||
startDate: 2026-05-01
|
||||
featured: true
|
||||
---
|
||||
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
title: "TimeNet CLI"
|
||||
description: "Command-line tool for managing TimeNet attendance tracking - clock in/out from terminal"
|
||||
pubDate: 2026-04-15
|
||||
status: "active"
|
||||
stack: ["Python", "CLI", "Automation"]
|
||||
featured: false
|
||||
---
|
||||
|
||||
# TimeNet CLI
|
||||
|
||||
A command-line interface for managing TimeNet attendance tracking. Clock in, clock out, and check status without opening the web interface.
|
||||
|
||||
## What It Does
|
||||
|
||||
TimeNet CLI automates daily time tracking workflows:
|
||||
- Clock in/out with a single command
|
||||
- Check current status (in/out, hours worked today)
|
||||
- View work history
|
||||
- Export reports
|
||||
|
||||
## Why I Built This
|
||||
|
||||
My employer uses TimeNet for attendance tracking. Opening a browser, navigating to the site, logging in, and clicking buttons for a simple clock-in felt like unnecessary friction.
|
||||
|
||||
I wanted a faster way: `timenet in` in the morning, `timenet out` when leaving. That's it.
|
||||
|
||||
## Stack
|
||||
|
||||
- **Python** for CLI logic
|
||||
- **Click** for command-line interface
|
||||
- **Requests** for API interaction
|
||||
- **Credential management** for secure token storage
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Clock in
|
||||
timenet in
|
||||
|
||||
# Clock out
|
||||
timenet out
|
||||
|
||||
# Check status
|
||||
timenet status
|
||||
|
||||
# View today's hours
|
||||
timenet today
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
Active. I use it daily for work attendance tracking.
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import Header from '../../components/layout/Header.astro';
|
||||
import Footer from '../../components/layout/Footer.astro';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const blogEntries = await getCollection('blog', ({ data }) => !data.draft);
|
||||
return blogEntries.map(entry => ({
|
||||
params: { slug: entry.slug },
|
||||
props: { entry },
|
||||
}));
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content } = await entry.render();
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title={entry.data.title}
|
||||
description={entry.data.description}
|
||||
>
|
||||
<Header />
|
||||
<main class="flex-1 max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<article>
|
||||
<header class="mb-8">
|
||||
<time class="text-sm text-text-tertiary block mb-2">
|
||||
{entry.data.publishDate.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
|
||||
<h1 class="text-4xl font-bold mb-4">{entry.data.title}</h1>
|
||||
|
||||
<p class="text-xl text-text-secondary mb-4">
|
||||
{entry.data.description}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{entry.data.tags.map((tag) => (
|
||||
<span class="inline-block px-3 py-1 rounded-full text-sm font-medium border bg-text-tertiary/10 text-text-secondary border-text-tertiary/20">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="prose prose-invert prose-lg max-w-none">
|
||||
<Content />
|
||||
</div>
|
||||
|
||||
<footer class="mt-12 pt-8 border-t border-text-tertiary/20">
|
||||
<a
|
||||
href="/blog"
|
||||
class="text-primary hover:text-secondary transition-colors font-medium"
|
||||
>
|
||||
← Back to all posts
|
||||
</a>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
<Footer />
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import Header from '../../components/layout/Header.astro';
|
||||
import Footer from '../../components/layout/Footer.astro';
|
||||
|
||||
const allPosts = await getCollection('blog', ({ data }) => !data.draft);
|
||||
const sortedPosts = allPosts.sort(
|
||||
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
|
||||
);
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Blog"
|
||||
description="Thoughts on building products, technical learnings, and the indie builder journey"
|
||||
>
|
||||
<Header />
|
||||
<main class="flex-1 max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<h1 class="text-4xl font-bold mb-8">Blog</h1>
|
||||
|
||||
<div class="space-y-8">
|
||||
{sortedPosts.map((post) => (
|
||||
<article class="bg-surface rounded-lg border border-text-tertiary/20 p-6 hover:border-primary/40 transition-colors">
|
||||
<a href={`/blog/${post.slug}`} class="block">
|
||||
<time class="text-sm text-text-tertiary block mb-2">
|
||||
{post.data.publishDate.toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
|
||||
<h2 class="text-2xl font-semibold mb-3 text-text-primary hover:text-primary transition-colors">
|
||||
{post.data.title}
|
||||
</h2>
|
||||
|
||||
<p class="text-text-secondary mb-4">
|
||||
{post.data.description}
|
||||
</p>
|
||||
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
{post.data.tags.map((tag) => (
|
||||
<span class="inline-block px-3 py-1 rounded-full text-sm font-medium border bg-text-tertiary/10 text-text-secondary border-text-tertiary/20">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<span class="text-primary font-medium">Read more →</span>
|
||||
</a>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{sortedPosts.length === 0 && (
|
||||
<p class="text-text-secondary text-center py-12">
|
||||
No posts yet. Check back soon!
|
||||
</p>
|
||||
)}
|
||||
</main>
|
||||
<Footer />
|
||||
</BaseLayout>
|
||||
Reference in New Issue
Block a user