Squashed commit of the following:

commit 6304df307b853f9a30d06307a99a77bc3b0392f4
Author: Ivan Dimitrov <ivan@idimitrov.dev>
Date:   Wed Nov 15 23:16:35 2023 +0200

    parcel lab case

commit 3e0a3648ab998e4754427da08aa9c539f043f3b4
Author: Ivan Dimitrov <ivan@idimitrov.dev>
Date:   Wed Nov 15 19:46:28 2023 +0200

    some more content

commit e91a56dac8c1ed0f50964265406b329621f712f3
Author: Ivan Dimitrov <ivan@idimitrov.dev>
Date:   Wed Nov 15 18:49:21 2023 +0200

    styles
This commit is contained in:
Ivan Dimitrov 2023-11-15 23:17:00 +02:00
parent 57218bd8e2
commit a3d71f7b42
9 changed files with 232 additions and 11 deletions

198
_content/cases/parcellab.md Normal file
View File

@ -0,0 +1,198 @@
---
title: Parcel Lab integration into international eCommerce app
goal: Integrate the Parcel Lab Post-purchase solution into all US and European WOSG websites
role: Plan, design and implement the integration
date: 2022 - 2023
z: 2
draft: false
---
> parcelLab is the only truly global enterprise post-purchase software provider, enabling brands to increase top-line revenue, decrease operational costs, and optimize the customer experience.
[Parcel Lab](https://parcellab.com/)
Parcel lab takes care of the post-purchase operations like order tracking, email notifications, delivery status updates, data processing and more so that businesses don't have to.
---
### Technical overview
This integration is straightforward thanks to the [amazing documentation](https://how.parcellab.works/docs/) provided by the Parcel Lab team.
You really want to use the API even though there's more options to submit data to their platform.
The data model is based on the [tracking](https://how.parcellab.works/docs/onboarding/data-model) - a data object having four fields for the delivery information. An order is composed of one or more trackings.
Once data is submitted, the platform starts an automated process where it groups the new trackings to their respective orders and starts listening for events like
"dispatch", "payment received" etc. to run custom actions. Each business can configure these events and actions so that they best match their operations.
For example an "order created" event could notify the customer that the order has started as well as deal with some other business logic in the background.
Their [order status page](https://how.parcellab.works/docs/track-and-communicate/order-status-page) is a convenient script that you can configure for your website.
The script reads the URL to find an order number so that it can fetch the most up-to-date information for that order and display it in an iFrame.
This system allows for a seamless, declarative event-based integration where the business takes care of the data and events (and sales) and parcelLab takes care of the rest.
---
### ParcelLab API details
Create the first order when a customer places it.
```hurl
POST https://api.parcellab.com/order/ # This is the method and the endpoint
user: business-uid # Header
token: access-token # Header
Content-Type: application/json # Header
{ # Body starts here
"xid": "id-of-delivery-before-tracking-number",
"destination_country_iso3": "DEU",
"client": "your-client-id",
"orderNo": "order1", # This is the order ID
"recipient": "Max Mustermann, parcelLab GmbH",
"recipient_notification": "Max",
"email": "max-mustermann@abc.xyz",
"street": "Landwehrstr. 39",
"zip_code": "80336",
"city": "München",
"billing_info": {
"name": "Max",
"phone": "1234",
"street": "Nice St. 69",
"city": "Nice",
"zip_code": "69420",
"country_iso3": "DEU"
},
"articles": [
{
"articleNo": "sku", # required
"articleName": "Some Nice Thingy", # required
"articleCategory": "Nice", # optional
"articleUrl": "https://example.com/nice", # optional
"articleImageUrl":"https://example.com/nice.png", # optional
"quantity": 69, # optional
},
],
"customFields": {
"someField": {
"anything": "in",
"here": [],
"nice": 69
}
}
}
```
This will reflect on the parcelLab dashboard.
Add a few trackings to that order once you get the delivery number from the courier.
```hurl
POST https://api.parcellab.com/track/
user: business-uid
token: access-token
Content-Type: application/json
{
"tracking_number": "1",
"courier": "dhl-germany",
"zip_code": "12345",
"deliveryNo": "1",
"orderNo": "order1" # This references the above order
}
POST https://api.parcellab.com/track/
user: business-uid
token: access-token
Content-Type: application/json
{
"tracking_number": "2",
"courier": "dhl-germany",
"zip_code": "12345",
"deliveryNo": "2",
"orderNo": "order1"
}
POST https://api.parcellab.com/track/
user: business-uid
token: access-token
Content-Type: application/json
{
"tracking_number": "3",
"courier": "dhl-germany",
"zip_code": "12345",
"deliveryNo": "3",
"orderNo": "order1"
}
```
The order now has 3 trackings. Parcel Lab integrates with many couriers to provide status updates.
Update the order for whatever reason.
```hurl
POST https://api.parcellab.com/order/
user: business-uid
token: access-token
Content-Type: application/json
{
"orderNo": "order1",
"customFields": {
"customerPleased": true
}
}
```
These updates can act as event notifications allowing for a declarative configuration.
```hurl
POST https://api.parcellab.com/order/
user: business-uid
token: access-token
Content-Type: application/json
{
"orderNo": "order1",
"send_date": "now" # Gets notified that the order has been dispatched
}
```
Complete shipment.
```hurl
POST https://api.parcellab.com/order/
user: business-uid
token: access-token
Content-Type: application/json
{
"orderNo": "order1",
"complete": true
}
```
---
All this can be viewed on the tracking page embedded anywhere.
```html
<div id="parcellab-track-and-trace">
<img src="https://cdn.parcellab.com/img/loading-spinner-1.gif" alt="loading" />
</div>
<script>
function plTrackAndTraceStart() {
window.parcelLabTrackAndTrace.initialize({
plUserId: TYPE_YOUR_USER_ID_HERE
});
var linkTag = document.createElement('link');
linkTag.rel = 'stylesheet';
linkTag.href = 'https://cdn.parcellab.com/css/v5/main.min.css';
document.getElementsByTagName('head')[0].appendChild(linkTag);
}
</script>
<script async onload="plTrackAndTraceStart()"
src="https://cdn.parcellab.com/js/v5/main.min.js"></script>
```
This shows a nice UI that can be [customized](https://how.parcellab.works/docs/track-and-communicate/order-status-page/configuration#additional-options).

View File

@ -3,7 +3,7 @@ title: Multi-tenant knowledge base website based on Google APIs
goal: Create a modern multi-tenant web app that lets users use their Google Drive as a knowledge base goal: Create a modern multi-tenant web app that lets users use their Google Drive as a knowledge base
role: Design and implement the web app role: Design and implement the web app
date: Jul 29, 2023 - Nov 5, 2023 date: Jul 29, 2023 - Nov 5, 2023
z: 5 z: 3
--- ---
This project aims to be a Google Drive frontend. It uses the Google APIs to fetch document data and display that data in a wiki-style web page. This project aims to be a Google Drive frontend. It uses the Google APIs to fetch document data and display that data in a wiki-style web page.

View File

@ -7,5 +7,8 @@ z: 1
draft: true draft: true
--- ---
[Wells Fargo](https://www.wellsfargo.com/) is a US based international financial institution operating in 35 countries and serving over 70 million people worldwide. [Source](https://en.wikipedia.org/wiki/Wells_Fargo)
They provide an

View File

@ -0,0 +1,13 @@
---
title: Static wiki website powered by Markdown
goal: Scrape an old wiki website and create a new one from the content
role: Scrape, design and implement the app
date: Jun 22, 2023 - Jun 27, 2023
z: 6
draft: true
---
Repo: [github.com](https://github.com/hearts-of-iron-2/wiki)
This project aims to revamp an old, unmaintained wiki website and bring it back to life.

View File

@ -16,11 +16,14 @@
system = "x86_64-linux"; system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
nvim = ide.nvim.${system} { nvim = ide.nvim.${system} {
plugins.lsp.servers = { plugins = {
html.enable = true; lsp.servers = {
tsserver.enable = true; html.enable = true;
jsonls.enable = true; tsserver.enable = true;
tailwindcss.enable = true; jsonls.enable = true;
tailwindcss.enable = true;
};
cmp-spell.enable = true;
}; };
}; };
buildInputs = with pkgs; [ buildInputs = with pkgs; [

View File

@ -53,3 +53,7 @@
@apply text-xl @apply text-xl
} }
.md a {
@apply underline text-teal-200 hover:text-cyan-500
}

View File

@ -10,7 +10,7 @@ import rehypeHighlight from "rehype-highlight";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import CopyButton from "$components/copy-button"; import CopyButton from "$components/copy-button";
import { getText } from "@/app/lib/react"; import { getText } from "$lib/react";
type Params = { type Params = {
slug: string[] slug: string[]
@ -95,7 +95,7 @@ export default function Content({ params }: Props) {
{role()} {role()}
{date()} {date()}
</div> </div>
<div className="w-full m-auto lg:w-3/4"> <div className="w-full m-auto lg:w-3/4 mt-10">
{ctnt()} {ctnt()}
</div> </div>
</div> </div>

View File

@ -1,9 +1,9 @@
import Link from "next/link"; import Link from "next/link";
import { getCases } from "../lib/content"; import { getCases } from "$lib/content";
const Cases = () => const Cases = () =>
<div className="p-20 w-3/4 mx-auto flex flex-col gap-4"> <div className="p-20 w-3/4 mx-auto flex flex-col gap-4">
{getCases().filter(c => !c.data.draft).sort(c => Number(c.data.z)).reverse().map((c) => c.data).map((d) => {getCases().filter(c => !c.data.draft).sort((a, b) => Number(b.data.z) - Number(a.data.z)).map((c) => c.data).map((d) =>
<div key={d.slug} className="w-full h-max flex justify-center rounded-lg border-2"> <div key={d.slug} className="w-full h-max flex justify-center rounded-lg border-2">
<Link className="btn flex flex-col w-full text-center" href={d.slug}> <Link className="btn flex flex-col w-full text-center" href={d.slug}>
<span className="text-lg px-6">{d.title}</span> <span className="text-lg px-6">{d.title}</span>

View File

@ -5,6 +5,6 @@ export const getText = (node: ReactNode | any) => {
if (!props) { if (!props) {
return node return node
} }
const c = props.children const c = props.children || ""
return typeof c === "string" ? c : c.map(getText).join("") return typeof c === "string" ? c : c.map(getText).join("")
} }