What a refactor of articles
This commit is contained in:
parent
33bf6769e4
commit
f156d4dacc
@ -1,29 +0,0 @@
|
||||
|
||||
<section id="personal">
|
||||
<h3>Personal Information</h3>
|
||||
<p>I was born on 26th of May in Košice, Slovakia and I still live here.</p>
|
||||
<h4>Hobbies:</h4>
|
||||
<p>
|
||||
I enjoy playing basketball with my friends. I also like to play other team sports like football and hockey.
|
||||
I also play squash and table tennis. Once I've won a competition in squash at my university.
|
||||
During summer I love water skiing and swimming in a nearby lake.
|
||||
<br />
|
||||
I am very passionate about music. I've also tried some software for composing music but I am not really hooked into that yet.
|
||||
From time to time I enjoy playing board games with my friends.
|
||||
</p>
|
||||
<h4>Interests:</h4>
|
||||
<p>
|
||||
I like to explore new technologies and I'm passionate about <em>Open Source movement</em>,
|
||||
<em>Internet of Things</em> applications and <em>Linux desktop evolution</em>.
|
||||
<br />
|
||||
I am interested in modern software architecture and <em>reactive programming</em>.
|
||||
<br />
|
||||
I've attended various <strong>tech conferences and hackathons</strong>. I like them for all of the fascinating ideas that might be invented.
|
||||
<br />
|
||||
I've given presentations on various topics related to <em>web development</em>. You can <a href='#presentations'>take a look at some of them here</a>.
|
||||
<br />
|
||||
I enjoy <strong>teaching and explaining</strong> how various technologies and techniques work to my colleagues for their better understanding.
|
||||
<br />
|
||||
I take advantage of <strong>test driven development</strong>.
|
||||
</p>
|
||||
</section><!--/personal-->
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { format } from 'date-fns'
|
||||
import type { PostContent } from 'src/routes/blog/content'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
import SvgIcon from './SvgIcon.svelte'
|
||||
import {
|
||||
boldClass,
|
||||
@ -22,7 +22,7 @@
|
||||
licenceText,
|
||||
} from './Footer.css'
|
||||
|
||||
export let latestPosts: PostContent[]
|
||||
export let latestPosts: ArticleContent[]
|
||||
</script>
|
||||
|
||||
<footer class="site-footer navigation-theme {siteFooterClass}">
|
@ -12,40 +12,48 @@
|
||||
portfolioPageNavigationLinksClass,
|
||||
selectedClass,
|
||||
} from './Nav.css'
|
||||
import { page } from "$app/stores"
|
||||
import { page } from '$app/stores'
|
||||
|
||||
$: segment = $page.url.pathname
|
||||
|
||||
let links = [
|
||||
{
|
||||
label: 'Introduction',
|
||||
url: '/',
|
||||
},
|
||||
{
|
||||
label: 'Blog',
|
||||
url: '/blog',
|
||||
},
|
||||
{
|
||||
label: 'Broadcasts',
|
||||
url: '/broadcasts',
|
||||
},
|
||||
// {
|
||||
// label: "Dev's Cookery",
|
||||
// url: '/cookery',
|
||||
// },
|
||||
{
|
||||
label: 'Portfolio',
|
||||
url: '/portfolio',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
<nav class={navigationClass}>
|
||||
<section class={navigationContentClass}>
|
||||
<ul class={navigationLinksClass}>
|
||||
<li>
|
||||
<a class={classNames({ [selectedClass]: segment === '/' })} href="/">
|
||||
Introduction
|
||||
</a>
|
||||
</li>
|
||||
<!-- for the blog link, we're using rel=prefetch so that Sapper prefetches
|
||||
the blog data when we hover over the link or tap it on a touchscreen -->
|
||||
<li>
|
||||
<a
|
||||
rel="prefetch"
|
||||
class={classNames({ [selectedClass]: segment.startsWith('/blog') })}
|
||||
href="/blog"
|
||||
>
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class={classNames({
|
||||
[selectedClass]: segment.startsWith('/portfolio'),
|
||||
})}
|
||||
href="/portfolio"
|
||||
>
|
||||
Portfolio
|
||||
</a>
|
||||
</li>
|
||||
{#each links as link}
|
||||
<li>
|
||||
<a
|
||||
rel="prefetch"
|
||||
class={classNames({ [selectedClass]: segment === link.url })}
|
||||
href={link.url}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<aside class="logo-section {logoSectionClass}">
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import svgSprite from '../svg/build/icons-sprite.svg'
|
||||
import svgSprite from '../../svg/build/icons-sprite.svg'
|
||||
export let className: string
|
||||
export let name: string
|
||||
</script>
|
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
interface ArticleDetails {
|
||||
title: string
|
||||
slug: string
|
||||
preview: string
|
||||
}
|
||||
export let segment: string
|
||||
export let article: ArticleDetails
|
||||
</script>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h2>
|
||||
<a rel="prefetch" href={`/${segment}/${article.slug}`}>{article.title}</a>
|
||||
</h2>
|
||||
</header>
|
||||
{@html article.preview}
|
||||
</article>
|
@ -2,26 +2,27 @@
|
||||
import { horizontalBorderTopClass } from '$lib/styles/scoops.css'
|
||||
|
||||
import { format } from 'date-fns'
|
||||
import type { PostContent } from '../../routes/blog/content'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
import {
|
||||
footerClass,
|
||||
publishedClass,
|
||||
publishedLabelClass,
|
||||
tagsListClass,
|
||||
tagsListLiClass,
|
||||
} from './ArticleFooter.css'
|
||||
} from './ArticlePreviewFooter.css'
|
||||
|
||||
export let post: PostContent
|
||||
export let segment: string
|
||||
export let article: ArticleContent
|
||||
</script>
|
||||
|
||||
<footer class="{footerClass} {horizontalBorderTopClass}">
|
||||
<div class="article-tags">
|
||||
{#if post.tags.length > 0}
|
||||
{#if article.tags.length > 0}
|
||||
<span class="lighten">Tags:</span>
|
||||
<ul class={tagsListClass}>
|
||||
{#each post.tags as tag}
|
||||
{#each article.tags as tag}
|
||||
<li class={tagsListLiClass}>
|
||||
<a href="/blog/tags/{tag}">{tag}</a>
|
||||
<a href="/{segment}/tags/{tag}">{tag}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
@ -29,8 +30,8 @@
|
||||
</div>
|
||||
<div class="created-at">
|
||||
<span class={publishedLabelClass}>Published on</span>
|
||||
<time datetime={post.date} class={publishedClass}>
|
||||
{format(new Date(post.date), "do MMMM',' y")}
|
||||
<time datetime={article.date} class={publishedClass}>
|
||||
{format(new Date(article.date), "do MMMM',' y")}
|
||||
</time>
|
||||
</div>
|
||||
</footer>
|
@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '$lib/components/articles/ArticlePreviewFooter/ArticlePreviewFooter.svelte'
|
||||
import Paginator from '$lib/components/paginator/Paginator.svelte'
|
||||
import { postListClass } from './ArticlePreviewList.css'
|
||||
import ArticlePreviewCard from '$lib/components/articles/ArticlePreviewCard/ArticlePreviewCard.svelte'
|
||||
import type { PaginationResult } from '$lib/pagination/pagination'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
|
||||
export let page: number
|
||||
export let pageSize: number
|
||||
export let filters: Record<string, string>
|
||||
export let posts: PaginationResult<ArticleContent>
|
||||
export let segment: string
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<Paginator
|
||||
{segment}
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</header>
|
||||
<ul class="post-list {postListClass}">
|
||||
{#each posts.items as article (article.slug)}
|
||||
<li>
|
||||
<ArticlePreviewCard {article} {segment} />
|
||||
<ArticleFooter {article} {segment} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<footer>
|
||||
<Paginator
|
||||
{segment}
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</footer>
|
@ -10,12 +10,11 @@
|
||||
|
||||
export const Divider = 'divider'
|
||||
|
||||
export let href: string
|
||||
export let segment: string
|
||||
export let page: number
|
||||
export let pageSize: number
|
||||
export let totalCount: number
|
||||
export let filters: Record<string, string>
|
||||
let paginatorPages: (number | typeof Divider)[]
|
||||
|
||||
$: paginatorPages = getPaginatorPages({ page, pageSize, totalCount })
|
||||
</script>
|
||||
@ -23,7 +22,7 @@
|
||||
<ul class={listClass}>
|
||||
{#if page !== 1}
|
||||
<li class="{listItemClass} ">
|
||||
<a class={pageLinkClass} href={createHref(href, filters, page - 1)}
|
||||
<a class={pageLinkClass} href={createHref(segment, filters, page - 1)}
|
||||
><</a
|
||||
>
|
||||
</li>
|
||||
@ -35,7 +34,7 @@
|
||||
<li class="{listItemClass} {activePage}">{pageNumber}</li>
|
||||
{:else}
|
||||
<li class="{listItemClass} ">
|
||||
<a class={pageLinkClass} href={createHref(href, filters, pageNumber)}
|
||||
<a class={pageLinkClass} href={createHref(segment, filters, pageNumber)}
|
||||
>{pageNumber}</a
|
||||
>
|
||||
</li>
|
||||
@ -43,7 +42,7 @@
|
||||
{/each}
|
||||
{#if page !== paginatorPages.length}
|
||||
<li class="{listItemClass} ">
|
||||
<a class={pageLinkClass} href={createHref(href, filters, page + 1)}
|
||||
<a class={pageLinkClass} href={createHref(segment, filters, page + 1)}
|
||||
>></a
|
||||
>
|
||||
</li>
|
@ -11,11 +11,11 @@ export function getPaginatorPages({
|
||||
page: number
|
||||
pageSize: number
|
||||
totalCount: number
|
||||
}) {
|
||||
}): (number | typeof Divider)[] {
|
||||
const maxLinksLength = 7
|
||||
const linksAroundActive = 2
|
||||
const totalPages = Math.ceil(totalCount / pageSize)
|
||||
const daco = range(1, totalPages + 1).reduce((acc, link) => {
|
||||
const daco = range(1, totalPages + 1).reduce<(number | typeof Divider)[]>((acc, link) => {
|
||||
const isFirst = link === 1
|
||||
const isLast = link === totalPages
|
||||
const isPageOnStart = page <= 3 && link < maxLinksLength
|
@ -11,7 +11,7 @@ import {
|
||||
|
||||
const { NODE_ENV } = process.env
|
||||
|
||||
export interface PostAttributes {
|
||||
export interface ArticleAttributes {
|
||||
layout: string
|
||||
title: string
|
||||
published: boolean
|
||||
@ -20,7 +20,7 @@ export interface PostAttributes {
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface PostContent extends PostAttributes {
|
||||
export interface ArticleContent extends ArticleAttributes {
|
||||
preview: string
|
||||
slug: string
|
||||
published: boolean
|
||||
@ -36,7 +36,7 @@ export async function getBlogListing(paginationQuery: PaginationQuery) {
|
||||
`_posts/blog/${file}`,
|
||||
'utf-8'
|
||||
)
|
||||
const parsedAttributes = fm<PostAttributes>(fileContent)
|
||||
const parsedAttributes = fm<ArticleAttributes>(fileContent)
|
||||
|
||||
const lineOfTextRegExp = /^(?:\w|\[).+/gm
|
||||
const lines = parsedAttributes.body
|
||||
@ -53,7 +53,7 @@ export async function getBlogListing(paginationQuery: PaginationQuery) {
|
||||
})
|
||||
)
|
||||
const filteredContents = pipe(
|
||||
sortBy<PostContent>(prop('date')),
|
||||
sortBy<ArticleContent>(prop('date')),
|
||||
(items) => reverse(items),
|
||||
filter<(typeof contents)[0]>((article) => article.published),
|
||||
filterAndCount(paginationQuery)
|
@ -17,7 +17,8 @@ export function dropAndTake<Item>({ offset = 0, limit = Infinity }) {
|
||||
) => Item[]
|
||||
}
|
||||
|
||||
export function filterByPropContains<Item>(filters: Record<string, string>) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function filterByPropContains<Item extends Record<string, any>>(filters: Record<string, string>) {
|
||||
return function (items: Item[]) {
|
||||
return items.filter((item) => {
|
||||
return Object.entries(filters).every(([fieldName, value]) =>
|
||||
@ -27,7 +28,8 @@ export function filterByPropContains<Item>(filters: Record<string, string>) {
|
||||
}
|
||||
}
|
||||
|
||||
export function filterAndCount<Item>({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function filterAndCount<Item extends Record<string, any>>({
|
||||
filters,
|
||||
...dropTakeParams
|
||||
}: PaginationQuery) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { LayoutData } from "./$types"
|
||||
import Nav from '../components/Nav.svelte'
|
||||
import Footer from '../components/Footer.svelte'
|
||||
import type { LayoutData } from './$types'
|
||||
import Nav from '$lib/components/Nav.svelte'
|
||||
import Footer from '$lib/components/Footer.svelte'
|
||||
import 'modern-normalize/modern-normalize.css'
|
||||
import '$lib/styles/global.css'
|
||||
import { mainContentClass } from './layout.css'
|
||||
|
@ -2,7 +2,7 @@ import type { LayoutLoad } from './$types'
|
||||
export const prerender = true
|
||||
|
||||
export const load = (async ({ fetch }) => {
|
||||
const blogPostsResponse = await fetch(`/blog/articles/pageSize/5.json`)
|
||||
const blogPostsResponse = await fetch(`/articles/pageSize/5.json`)
|
||||
const blogPostsContent = await blogPostsResponse.json()
|
||||
|
||||
return {
|
||||
|
@ -3,7 +3,7 @@ import {
|
||||
parseParams,
|
||||
} from '$lib/pagination/dropTakeParams'
|
||||
import { json } from '@sveltejs/kit'
|
||||
import { getBlogListing } from '../../content'
|
||||
import { getBlogListing } from '$lib/content/articleContentListing'
|
||||
import type { RequestHandler } from './$types'
|
||||
|
||||
export const prerender = true
|
@ -1,12 +1,10 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '../../../components/blog/ArticleFooter.svelte'
|
||||
import Paginator from '../../../components/paginator/Paginator.svelte'
|
||||
import { postListClass, seeAllClass } from './page.css'
|
||||
import type { PageData } from './$types'
|
||||
import ArticlePreviewList from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.svelte'
|
||||
import { seeAllClass } from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.css'
|
||||
|
||||
export let data: PageData
|
||||
let { posts, filters, page, pageSize } = data
|
||||
$: ({ posts, filters, page, pageSize } = data)
|
||||
$: ({ posts, filters } = data)
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@ -30,36 +28,5 @@
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<header>
|
||||
<Paginator
|
||||
href="blog"
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</header>
|
||||
<ul class="post-list {postListClass}">
|
||||
{#each posts.items as post (post.slug)}
|
||||
<li>
|
||||
<article>
|
||||
<header>
|
||||
<h2>
|
||||
<a rel="prefetch" href="/blog/{post.slug}">{post.title}</a>
|
||||
</h2>
|
||||
</header>
|
||||
{@html post.preview}
|
||||
</article>
|
||||
<ArticleFooter {post} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<footer>
|
||||
<Paginator
|
||||
href="blog"
|
||||
{page}
|
||||
{pageSize}
|
||||
{filters}
|
||||
totalCount={posts.totalCount}
|
||||
/>
|
||||
</footer>
|
||||
|
||||
<ArticlePreviewList {...data} segment="blog" />
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { parseParams } from '$lib/pagination/dropTakeParams'
|
||||
import type { PageLoad } from './$types'
|
||||
import type { PostContent } from './../content'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
import type { PaginationResult } from '$lib/pagination/pagination'
|
||||
|
||||
export const load = (async ({ fetch, params }) => {
|
||||
const { page = 1, pageSize = 7, ...filters } = parseParams(params.params)
|
||||
const articleResponse = await fetch(
|
||||
`/blog/articles/${params.params ? params.params : 'index'}.json`
|
||||
`/articles/${params.params ? params.params : 'index'}.json`
|
||||
).then((r) => r.json())
|
||||
|
||||
return {
|
||||
posts: articleResponse.posts as PaginationResult<PostContent>,
|
||||
posts: articleResponse.posts as PaginationResult<ArticleContent>,
|
||||
page: Number(page),
|
||||
pageSize,
|
||||
filters,
|
||||
|
@ -2,8 +2,8 @@ import { readFile } from 'fs'
|
||||
import { promisify } from 'util'
|
||||
import fm from 'front-matter'
|
||||
import { parseField } from '../../../markdown/parse-markdown'
|
||||
import { error, json } from '@sveltejs/kit'
|
||||
import type { PostAttributes } from '../content'
|
||||
import { error } from '@sveltejs/kit'
|
||||
import type { ArticleAttributes } from '$lib/content/articleContentListing'
|
||||
import type { PageServerLoad } from './$types'
|
||||
|
||||
export const prerender = true
|
||||
@ -24,7 +24,7 @@ export const load = (async ({ params: { slug } }) => {
|
||||
throw e
|
||||
}
|
||||
|
||||
const parsedPost = fm<PostAttributes>(postSource)
|
||||
const parsedPost = fm<ArticleAttributes>(postSource)
|
||||
|
||||
const post = parseField<SinglePost>('body')({
|
||||
...parsedPost.attributes,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import ArticleFooter from '../../../components/blog/ArticleFooter.svelte'
|
||||
import ArticleFooter from '$lib/components/articles/ArticlePreviewFooter/ArticlePreviewFooter.svelte'
|
||||
import type { PageData } from './$types'
|
||||
import { contentClass } from './page.css'
|
||||
|
||||
@ -15,4 +15,4 @@
|
||||
<div class="content {contentClass}">
|
||||
{@html data.body}
|
||||
</div>
|
||||
<ArticleFooter post={data} />
|
||||
<ArticleFooter article={data} segment="blog" />
|
||||
|
30
src/routes/broadcasts/[...params]/+page.svelte
Normal file
30
src/routes/broadcasts/[...params]/+page.svelte
Normal file
@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types'
|
||||
import ArticlePreviewList from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.svelte'
|
||||
import { seeAllClass } from '$lib/components/articles/ArticlePreviewList/ArticlePreviewList.css'
|
||||
|
||||
export let data: PageData
|
||||
$: ({ posts, filters } = data)
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Broadcasts @michalvankodev</title>
|
||||
</svelte:head>
|
||||
|
||||
{#if posts.items.length === 0}
|
||||
<p class="no-posts">You've found void in the space.</p>
|
||||
{:else}
|
||||
<h1>
|
||||
{#if filters.tags}
|
||||
<em>{filters.tags}</em>
|
||||
{/if}
|
||||
Broadcasts
|
||||
</h1>
|
||||
{#if filters.tags}
|
||||
<div class={seeAllClass}>
|
||||
<a href="/broadcasts">See all broadcasts</a>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
<ArticlePreviewList {...data} segment="broadcasts" />
|
18
src/routes/broadcasts/[...params]/+page.ts
Normal file
18
src/routes/broadcasts/[...params]/+page.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { parseParams } from '$lib/pagination/dropTakeParams'
|
||||
import type { PageLoad } from './$types'
|
||||
import type { ArticleContent } from '$lib/content/articleContentListing'
|
||||
import type { PaginationResult } from '$lib/pagination/pagination'
|
||||
|
||||
export const load = (async ({ fetch, params }) => {
|
||||
const { page = 1, pageSize = 7, ...filters } = parseParams(params.params)
|
||||
const articleResponse = await fetch(
|
||||
`/articles/${params.params ? params.params : 'index'}.json`
|
||||
).then((r) => r.json())
|
||||
|
||||
return {
|
||||
posts: articleResponse.posts as PaginationResult<ArticleContent>,
|
||||
page: Number(page),
|
||||
pageSize,
|
||||
filters,
|
||||
}
|
||||
}) satisfies PageLoad
|
@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Work from '../../components/portfolio/work.svelte'
|
||||
import Project from '../../components/portfolio/project.svelte'
|
||||
import Presentation from '../../components/portfolio/presentation.svelte'
|
||||
import Work from './components/work.svelte'
|
||||
import Project from './components/project.svelte'
|
||||
import Presentation from './components/presentation.svelte'
|
||||
import type { PageData } from './$types'
|
||||
|
||||
import { listClass, listItemClass, nameTagClass } from './page.css'
|
||||
|
39
src/routes/portfolio/components/personal.svelte
Normal file
39
src/routes/portfolio/components/personal.svelte
Normal file
@ -0,0 +1,39 @@
|
||||
<section id="personal">
|
||||
<h3>Personal Information</h3>
|
||||
<p>I was born on 26th of May in Košice, Slovakia and I still live here.</p>
|
||||
<h4>Hobbies:</h4>
|
||||
<p>
|
||||
I enjoy playing basketball with my friends. I also like to play other team
|
||||
sports like football and hockey. I also play squash and table tennis. Once
|
||||
I've won a competition in squash at my university. During summer I love
|
||||
water skiing and swimming in a nearby lake.
|
||||
<br />
|
||||
I am very passionate about music. I've also tried some software for composing
|
||||
music but I am not really hooked into that yet. From time to time I enjoy playing
|
||||
board games with my friends.
|
||||
</p>
|
||||
<h4>Interests:</h4>
|
||||
<p>
|
||||
I like to explore new technologies and I'm passionate about <em
|
||||
>Open Source movement</em
|
||||
>,
|
||||
<em>Internet of Things</em> applications and
|
||||
<em>Linux desktop evolution</em>.
|
||||
<br />
|
||||
I am interested in modern software architecture and
|
||||
<em>reactive programming</em>.
|
||||
<br />
|
||||
I've attended various <strong>tech conferences and hackathons</strong>. I
|
||||
like them for all of the fascinating ideas that might be invented.
|
||||
<br />
|
||||
I've given presentations on various topics related to
|
||||
<em>web development</em>. You can
|
||||
<a href="#presentations">take a look at some of them here</a>.
|
||||
<br />
|
||||
I enjoy <strong>teaching and explaining</strong> how various technologies
|
||||
and techniques work to my colleagues for their better understanding.
|
||||
<br />
|
||||
I take advantage of <strong>test driven development</strong>.
|
||||
</p>
|
||||
</section>
|
||||
<!--/personal-->
|
Loading…
Reference in New Issue
Block a user