Merge branch 'main' of github.com:jcalixte/blog

This commit is contained in:
Julien Calixte
2022-12-06 10:24:36 +01:00
28 changed files with 1858 additions and 1046 deletions

2
.gitignore vendored
View File

@@ -16,6 +16,8 @@ dist-ssr
/cypress/videos/ /cypress/videos/
/cypress/screenshots/ /cypress/screenshots/
/~
# Editor directories and files # Editor directories and files
.vscode .vscode
!.vscode/extensions.json !.vscode/extensions.json

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"semi": false
}

24
components.d.ts vendored
View File

@@ -1,17 +1,23 @@
// generated by unplugin-vue-components // generated by unplugin-vue-components
// We suggest you to commit this file into source control // We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
declare module 'vue' { export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
AboutMe: typeof import('./src/components/presentation/AboutMe.vue')['default'] AboutMe: typeof import('./src/components/presentation/about-me.vue')['default']
AppHeader: typeof import('./src/components/AppHeader.vue')['default'] AppHeader: typeof import('./src/components/app-header.vue')['default']
BlogPosts: typeof import('./src/components/posts/BlogPosts.vue')['default'] BlogPosts: typeof import('./src/components/posts/blog-posts.vue')['default']
Island: typeof import('./node_modules/.pnpm/iles@0.7.40_sass@1.53.0/node_modules/iles/dist/client/app/components/Island.vue')['default'] ForgettingCurve: typeof import('./src/components/smart-notes/forgetting-curve.vue')['default']
JulienCalixte: typeof import('./src/components/core/JulienCalixte.vue')['default'] Island: typeof import('./node_modules/.pnpm/iles@0.8.7_sass@1.56.1/node_modules/iles/dist/client/app/components/Island.vue')['default']
JulienCalixte: typeof import('./src/components/core/julien-calixte.vue')['default']
MyBooks: typeof import('./src/components/presentation/my-books.vue')['default']
MyProjects: typeof import('./src/components/presentation/my-projects.vue')['default']
ProductionFlow: typeof import('./src/components/flow/ProductionFlow.vue')['default'] ProductionFlow: typeof import('./src/components/flow/ProductionFlow.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Welcome: typeof import('./src/components/Welcome.vue')['default'] Welcome: typeof import('./src/components/Welcome.vue')['default']
} }
} }
export { }

View File

@@ -1,6 +1,7 @@
import { defineConfig } from "iles"; import { defineConfig } from "iles"
import prism from "@islands/prism"
export default defineConfig({ export default defineConfig({
siteUrl: "https://juliencalixte.eu", siteUrl: "https://juliencalixte.eu",
modules: ["@islands/headings"], modules: ["@islands/headings", prism()],
}); })

View File

@@ -11,16 +11,17 @@
"node": ">= 16.0.0" "node": ">= 16.0.0"
}, },
"devDependencies": { "devDependencies": {
"@islands/headings": "^0.1.2", "@islands/headings": "^0.8.1",
"@islands/prism": "^0.1.1", "@islands/prism": "^0.8.0",
"iles": "^0.7.40", "iles": "^0.8.7",
"sass": "^1.53.0", "sass": "^1.56.1",
"typescript": "^4.7.4", "typescript": "^4.9.3",
"vitest": "^0.16.0", "vitest": "^0.25.3",
"vue": "^3.2.37", "vue": "^3.2.45",
"vue-tsc": "^0.38.2" "vue-tsc": "^1.0.11"
}, },
"dependencies": { "dependencies": {
"pinia": "^2.0.14" "chart.xkcd": "^1.1.13",
"pinia": "^2.0.27"
} }
} }

1892
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

1
shim.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module "chart.xkcd"

View File

@@ -1,10 +1,10 @@
import { pinia } from "@/store/store"; import { pinia } from "@/store/store"
import { defineApp } from "iles"; import { defineApp } from "iles"
import "@/assets/base.scss"; import "@/assets/base.scss"
export default defineApp({ export default defineApp({
enhanceApp({ app }) { enhanceApp({ app }) {
app.use(pinia); app.use(pinia)
}, },
head() { head() {
return { return {
@@ -14,6 +14,6 @@ export default defineApp({
href: "https://fonts.googleapis.com/css2?family=Gulzar&family=Meow+Script&display=swap", href: "https://fonts.googleapis.com/css2?family=Gulzar&family=Meow+Script&display=swap",
}, },
], ],
}; }
}, },
}); })

View File

@@ -1,3 +1,5 @@
@import "prism.theme";
:root { :root {
--vt-c-white: #ffffff; --vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8; --vt-c-white-soft: #f8f8f8;
@@ -56,7 +58,6 @@
*::after { *::after {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
position: relative;
font-weight: normal; font-weight: normal;
} }
@@ -84,7 +85,6 @@ h3,
h4, h4,
h5, h5,
h6 { h6 {
font-variant: small-caps;
color: var(--primary); color: var(--primary);
} }
@@ -97,7 +97,8 @@ h2 {
} }
.julien-calixte { .julien-calixte {
font-family: "Meow Script", "Lucida Sans", "Lucida Sans Regular", Geneva, Verdana, sans-serif; font-family: "Meow Script", "Lucida Sans", "Lucida Sans Regular", Geneva,
Verdana, sans-serif;
} }
strong { strong {

187
src/assets/prism.theme.scss Normal file
View File

@@ -0,0 +1,187 @@
/*
Name: Duotone Sea
Author: by Simurai, adapted from DuoTone themes by Simurai for Atom (http://simurai.com/projects/2016/01/01/duotone-themes)
Conversion: Bram de Haan (http://atelierbram.github.io/Base2Tone-prism/output/prism/prism-base2tone-sea-dark.css)
Generated with Base16 Builder (https://github.com/base16-builder/base16-builder)
*/
code[class*="language-"],
pre[class*="language-"] {
font-family: Consolas, Menlo, Monaco, "Andale Mono WT", "Andale Mono",
"Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
"Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L",
"Courier New", Courier, monospace;
font-size: 14px;
line-height: 1.375;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
background: #1d262f;
color: #57718e;
}
pre > code[class*="language-"] {
font-size: 1em;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #004a9e;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #004a9e;
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: 0.5em 0;
overflow: auto;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: 0.1em;
border-radius: 0.3em;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #4a5f78;
}
.token.punctuation {
color: #4a5f78;
}
.token.namespace {
opacity: 0.7;
}
.token.tag,
.token.operator,
.token.number {
color: #0aa370;
}
.token.property,
.token.function {
color: #57718e;
}
.token.tag-id,
.token.selector,
.token.atrule-id {
color: #ebf4ff;
}
code.language-javascript,
.token.attr-name {
color: #7eb6f6;
}
code.language-css,
code.language-scss,
.token.boolean,
.token.string,
.token.entity,
.token.url,
.language-css .token.string,
.language-scss .token.string,
.style .token.string,
.token.attr-value,
.token.keyword,
.token.control,
.token.directive,
.token.unit,
.token.statement,
.token.regex,
.token.atrule {
color: #47ebb4;
}
.token.placeholder,
.token.variable {
color: #47ebb4;
}
.token.deleted {
text-decoration: line-through;
}
.token.inserted {
border-bottom: 1px dotted #ebf4ff;
text-decoration: none;
}
.token.italic {
font-style: italic;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.important {
color: #7eb6f6;
}
.token.entity {
cursor: help;
}
pre > code.highlight {
outline: 0.4em solid #34659d;
outline-offset: 0.4em;
}
/* overrides color-values for the Line Numbers plugin
* http://prismjs.com/plugins/line-numbers/
*/
.line-numbers.line-numbers .line-numbers-rows {
border-right-color: #1f2932;
}
.line-numbers .line-numbers-rows > span:before {
color: #2c3847;
}
/* overrides color-values for the Line Highlight plugin
* http://prismjs.com/plugins/line-highlight/
*/
.line-highlight.line-highlight {
background: rgba(10, 163, 112, 0.2);
background: -webkit-linear-gradient(
left,
rgba(10, 163, 112, 0.2) 70%,
rgba(10, 163, 112, 0)
);
background: linear-gradient(
to right,
rgba(10, 163, 112, 0.2) 70%,
rgba(10, 163, 112, 0)
);
}

View File

@@ -5,6 +5,10 @@
<about-me /> <about-me />
<blog-posts /> <blog-posts />
<my-projects />
<my-books />
</section> </section>
</template> </template>
@@ -12,5 +16,6 @@
.welcome { .welcome {
max-width: 800px; max-width: 800px;
margin: auto; margin: auto;
padding: 1rem;
} }
</style> </style>

View File

@@ -1,25 +0,0 @@
<script setup lang="ts">
const fontSize = {
small: "12px",
normal: "inherit",
big: "44px",
};
interface Props {
size?: keyof typeof fontSize;
}
const props = withDefaults(defineProps<Props>(), { size: "normal" });
const style = `font-size: ${fontSize[props.size]}`;
</script>
<template>
<span class="julien-calixte" :style="style">Julien Calixte</span>
</template>
<style scoped lang="scss">
.julien-calixte {
font-variant: normal;
}
</style>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
const fontSize = {
small: "12px",
normal: "inherit",
big: "44px",
}
interface Props {
version?: "compact" | "full"
size?: keyof typeof fontSize
}
const props = withDefaults(defineProps<Props>(), {
version: "full",
size: "normal",
})
const style = `font-size: ${fontSize[props.size]}`
</script>
<template>
<span class="julien-calixte" :style="style"
>Julien<template v-if="version === 'full'"> Calixte</template></span
>
</template>
<style scoped lang="scss">
.julien-calixte {
font-variant: normal;
}
</style>

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
import { usePosts } from "@/hooks/usePosts.hook";
const posts = usePosts();
</script>
<template>
<div class="blog-posts">
<h2>Last posts</h2>
<div v-for="post in posts" :key="post.href">
<h3>
<a :href="post.href">{{ post.title }}</a>
</h3>
</div>
</div>
</template>
<style scoped lang="scss">
.blog-posts {
h3 {
font-weight: bold;
}
}
</style>

View File

@@ -0,0 +1,14 @@
<script setup lang="ts">
import { usePosts } from "@/hooks/usePosts.hook";
const posts = usePosts();
</script>
<template>
<div class="blog-posts">
<h2>Last posts</h2>
<h3 v-for="post in posts" :key="post.href">
- <a :href="post.href">{{ post.title }}</a>
</h3>
</div>
</template>

View File

@@ -1,7 +1,9 @@
<script setup lang="ts"></script> <script setup lang="ts"></script>
<template> <template>
<h1>Hi! I'm <julien-calixte />. A mobile & web developer.</h1> <h1>
Hi! I'm <julien-calixte version="compact" />. A mobile & web developer.
</h1>
<section class="about-me"> <section class="about-me">
<p>I am into building things with code.</p> <p>I am into building things with code.</p>
</section> </section>

View File

@@ -0,0 +1,40 @@
<template>
<section>
<h2>My books</h2>
<p>I used to read so little, but in 2021, I decided to take example of CGP Grey and add as one of my theme to read
more and write more.
</p>
<iframe src="https://www.youtube-nocookie.com/embed/NVGuFdX5guE" title="Your Theme by CGP Grey" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen></iframe>
<p>
So far, I'm pretty happy with how many books I've read, and they are all good!
</p>
<ul>
<li><a href="https://www.soenkeahrens.de/en/takesmartnotes">How to take smart notes</a></li>
<li><a href="https://resilientwebdesign.com/">Resilient web design</a></li>
<li><a href="https://www.babelio.com/livres/Zinsser-On-Writing-Well/795524">On writing well</a></li>
<li><a href="https://juliagalef.com/">The Scout Mindset: Why Some People See Things Clearly and Others Dont</a>
</li>
<li><a href="https://www.oreilly.com/library/view/accelerate/9781457191435/">Accelerate</a></li>
<li><a href="https://www.amazon.fr/Immune-Kurzgesagt-gorgeously-illustrated-immune/dp/1529360684">Immune</a></li>
<li><a
href="https://www.routledge.com/The-Field-Guide-to-Understanding-Human-Error/Dekker/p/book/9781472439055">The
field guide of understanding 'human errors'</a></li>
<li><a href="https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow">Thinking fast and slow</a></li>
</ul>
</section>
</template>
<style scoped>
iframe {
width: 100%;
height: 500px;
max-height: 35vh;
}
</style>

View File

@@ -0,0 +1,22 @@
<template>
<section>
<h2>My projects</h2>
<ul>
<li>
<a href="https://litenote.space/" target="_blank" rel="noopener noreferrer">Lite note</a>: highly inspired by <a
href="https://notes.andymatuschak.org/About_these_notes" target="_blank" rel="noopener noreferrer">Andy
Matuschak notes' website</a>, I wanted to have a
clean design where I can explore my notes I take on VS Code.
</li>
<li><a href="https://www.npmjs.com/package/retrobus" target="_blank" rel="noopener noreferrer">retrobus</a>: an
event bus lib I made for fun with a little
extra retroactive feature.</li>
<li>
<a href="https://www.npmjs.com/package/vue-pwa-asset-generator" target="_blank" rel="noopener noreferrer">Vue
PWA Asset generator</a>: a cli command (now
a bit out of date) that generates the multiple favicons needed for a Progressive Web App.
</li>
</ul>
</section>
</template>

View File

@@ -0,0 +1,33 @@
<script setup lang="ts">
import chart from 'chart.xkcd'
import { onMounted } from 'vue';
onMounted(() => {
const svg = document.querySelector('.forgetting-curve')
new chart.Line(svg, {
title: 'The forgetting curve',
xLabel: '# days',
yLabel: 'Retention',
data: {
labels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],
datasets: [{
label: 'Reality',
data: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
}],
}
})
})
</script>
<template>
<svg class="forgetting-curve"></svg>
</template>
<style scoped>
svg {
min-height: 300px;
width: 500px;
margin: auto;
}
</style>

View File

@@ -1,35 +1,20 @@
import { computed } from "vue"; import { Post } from "@/modules/post/entities/Post"
import { computed } from "vue"
interface Post { const byMostRecentFirst = (a: Post, b: Post) => {
filename: string; if (!b.date) {
lastUpdated: Date; return 1
href: string;
title: string;
meta: {
filename: string;
lastUpdated: Date;
href: string;
};
frontmatter: {
title: string;
publishedAt?: Date;
};
} }
const byDate = (a: Post, b: Post) => { if (!a.date) {
if (!b.frontmatter.publishedAt) { return 1
return -1;
} }
if (!a.frontmatter.publishedAt) { return a.date <= b.date ? 1 : -1
return 1;
} }
return a.frontmatter.publishedAt <= b.frontmatter.publishedAt ? -1 : 1;
};
export const usePosts = () => { export const usePosts = () => {
const posts = $(useDocuments<Post>("@/pages/posts")); const posts = $(useDocuments<Post>("@/pages/posts"))
return computed(() => posts.sort(byDate)); return computed(() => posts.sort(byMostRecentFirst))
}; }

View File

@@ -1,7 +1,13 @@
<script setup lang="ts">
const { frontmatter } = usePage()
</script>
<template> <template>
<div class="post"> <div class="post">
<app-header /> <app-header />
<img id="main-illustration" v-if="frontmatter.illustration" :src="frontmatter.illustration" alt="Main illustration">
<article id="main-article"> <article id="main-article">
<slot /> <slot />
</article> </article>
@@ -9,8 +15,34 @@
</template> </template>
<style lang="scss"> <style lang="scss">
#main-illustration {
object-fit: contain;
}
article#main-article { article#main-article {
max-width: 650px; max-width: 680px;
margin: auto;
padding: 1rem;
}
h1 {
line-height: 1em;
}
h2,
h3,
h4,
h5,
h6,
h7 {
margin-top: 1rem;
margin-bottom: 0.5rem;
}
img {
width: 100%;
max-height: 40vh;
display: flex;
margin: auto; margin: auto;
} }
</style> </style>

View File

@@ -1,33 +1,37 @@
import { pinia } from "@/store/store"; import { pinia } from "@/store/store"
import { defineStore } from "pinia"; import { defineStore } from "pinia"
interface Step { interface Step {
name: string; name: string
prerequisites: string[]; intention: string
outputs: string[]; responsible: string
prerequisites: string[]
outputs: string[]
} }
interface State { interface State {
steps: Step[]; steps: Step[]
} }
const initialState: State = { const initialState: State = {
steps: [ steps: [
{ {
name: "In production", name: "In production",
intention: "Deliver feature to the user",
responsible: "Product owner",
prerequisites: [], prerequisites: [],
outputs: ["Users can use the app"], outputs: ["Users can use the app"],
}, },
], ],
}; }
const useStore = defineStore("production-flow", { const useStore = defineStore("production-flow", {
state: () => ({ ...initialState }), state: () => ({ ...initialState }),
actions: { actions: {
addStep(step: Step) { addStep(step: Step) {
this.$state.steps = [step, ...this.$state.steps]; this.$state.steps = [step, ...this.$state.steps]
}, },
}, },
}); })
export const useProductionFlow = () => useStore(pinia); export const useProductionFlow = () => useStore(pinia)

View File

@@ -0,0 +1,18 @@
export interface Post {
filename: string
lastUpdated: Date
href: string
title: string
date: Date
illustration: string
meta: {
filename: string
lastUpdated: Date
href: string
}
frontmatter: {
title: string
publishedAt?: Date
}
draft?: boolean
}

View File

@@ -0,0 +1,214 @@
---
title: CRC Cards as training material
layout: post
date: 2022-12-03
draft: true
---
# CRC Cards as training material
CRC Card: **C**lass name, **R**esponsibilities, **C**ollaborators.
[CRC Cards](https://en.wikipedia.org/wiki/Class-responsibility-collaboration_card) are a teaching tool on how to design software. They were proposed by Kent Beck and Ward Cunningham, and, hell yeah it's useful.
When I'm talking with tech leaders, my goal is to sharpen our vision about the software we are working on. When developers implemente features, I often see responsability leaks: the feature works, but the component are hard to read, hard to reuse and we pile up technical debt too quickly.
CRC cards are a great tool to focus the discussion and to aknowledge the fact that the developer shared her global vision to each individual local component who must rely only on its local scope. Let's take an example of one discussion.
## The horrible `UserBookmarks` component 😨
Disclaimer: we're about to look at a very ugly code that is definetely doing too many things. The purpose of the discussion I have is to show to the tech lead that we really want to prevent this to happen and it is her mission to standardize it with her team.
Let's say we have a `UserBookmarks` component. Its role is to display a list of bookmarks the user saved.
```tsx
interface Props {
user: User
}
export const UserBookmarks: FunctionComponent<Props> = ({ user }) => {
const [bookmarks, setBookmarks] = useState<Bookmark[]>([])
const [showAddBookmarkModal, setShowAddBookmarkModal] = useState(false)
const [isLoading, setIsLoading] = useState(false)
useEffect(() => {
setIsLoading(true)
try {
const userBookmark = await fetch(`/users/${user.id}/bookmarks`, {
method: 'GET'
})
setBookmarks(userBookmarks)
} catch (error) {
setBookmarks([])
} finally {
setIsLoading(false)
}
}, [])
const addBookmarkToUser = async ({ bookmark }) => {
setIsLoading(true)
try {
const newBookmark = await fetch(`/users/${user.id}/bookmarks`, {
method: 'POST',
body: JSON.stringify({ bookmark })
})
setBookmarks([...bookmarks, newBookmark])
} catch (error) {
console.warn(error);
} finally {
setIsLoading(false)
}
}
return <div className="user-bookmarks">
<Title title={strings['frontoffice.bookmark.user_bookmarks']} />
{toolList}
<PrimaryButton
text={strings['frontoffice.bookmark.all_bookmarks']}
onClick={() => setShowAddBookmarkModal(true))}
alt={'strings['frontoffice.bookmark.add_bookmarks']'}
image={<FontAwesomeIcon icon={['fas', 'plus']} color="white" />}
/>
<AddBookmarkModal
visible={showAddBookmarkModal}
onClose={() => setShowAddBookmarkModal(false)}
tools={availableCustomTools}
onBookmarkAdd={addBookmarkToUser}
/>
</div>
}
```
```tsx
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { gsap } from 'gsap';
import differenceBy from 'lodash/differenceBy';
import React, { useCallback, useEffect, useState } from 'react';
import { PrimaryButton } from 'components/CustomButton/CustomButton';
import strings from 'components/Localization/Localisation';
import { Title } from 'components/Title/Title';
import { useLanguageContext } from 'context/LanguageContext';
import {
addToolToUser,
fetchToolsForUser,
removeToolFromUser,
} from 'data/actions/ToolActions';
import { CodeIso } from 'types/Language';
import { Tool } from 'types/Tool';
import { Tools } from 'types/Tools';
import { Ui } from 'types/Ui';
import { User } from 'types/User';
import AddToolModal from './AddToolModal/AddToolModal';
import { TilesSkeleton } from './TilesSkeleton';
import { ToolItem } from './Tool/ToolItem';
export const Toolbox: React.FC = ({
user,
ui,
customTools,
userTools,
indispensableTools,
userIsUpdating,
getToolData,
addToolToUser,
removeToolFromUser,
}) => {
const { language } = useLanguageContext();
const [showAddToolModal, setShowAddToolModal] = useState(false);
const [showDeleteTool, setShowDeleteTool] = useState(false);
const [tileAnimation, setTileAnimation] = useState<GSAPTween>();
const [fakeTiles, setFakeTiles] = useState<unknown[]>([]);
const [toolList, setToolList] = useState<JSX.Element>();
const userId = user['@id'];
const uiLoading = ui.loading;
const availableCustomTools = differenceBy(customTools, userTools, '@id');
const startLoadAnimation = (): void => {
setTileAnimation(
gsap.to(fakeTiles, {
duration: 0.8,
opacity: 0.35,
yoyo: true,
repeat: -1,
stagger: 0.025,
}),
);
};
const stopLoadingAnimation = useCallback((): void => {
tileAnimation?.kill();
}, [tileAnimation]);
const addBookmark = (): void => {
setShowAddToolModal(true);
setShowDeleteTool(false);
};
useEffect(() => {
startLoadAnimation();
if (userId) {
getToolData(user, language);
}
}, [userId, language]);
useEffect(() => {
if (!uiLoading) {
stopLoadingAnimation();
setToolList(
<div className="tool-list">
{indispensableTools.map(tool => (
<ToolItem key={tool['@id']} data={tool} showDeleteButton={false} />
))}
{userTools.map(tool => (
<ToolItem
key={tool['@id']}
data={tool}
showDeleteButton={showDeleteTool}
onDelete={() => removeToolFromUser(tool)}
/>
))}
</div>,
);
} else {
setToolList(<TilesSkeleton setFakeTiles={setFakeTiles} numberOfTiles={16} />);
}
}, [uiLoading]);
return (
<div className="Toolbox">
<Title title={strings['frontoffice.toolbox.my_tools']} />
{toolList}
<PrimaryButton
text={strings['frontoffice.toolbox.all_apps']}
onClick={addBookmark}
alt={'see all apps button'}
image={<FontAwesomeIcon icon={['fas', 'arrow-right']} color="white" />}
positionOfImage="right"
disabled={true}
/>
<AddToolModal
visible={showAddToolModal}
onClose={() => setShowAddToolModal(false)}
tools={availableCustomTools}
onToolAdd={addToolToUser}
userIsUpdating={userIsUpdating}
/>
</div>
);
};
```

View File

@@ -1,13 +1,21 @@
--- ---
title: An introduction to the feature factory title: An introduction to the feature factory
layout: post layout: post
date: 2022-11-19
draft: true
--- ---
# {title} # An introduction to the feature factory
From my experience, project failures mainly come by having mistrust between teams. Lead time due to team availability to solve dependencies increases. From my experience, project failures mainly come from having mistrust between teams. Therefor lead time due to team availability to solve dependencies increases.
In this post, I would like to tell you what helped me, how we succeed to engage all the skills needed to develop a product. We will talk about agility, lean. A kanban is a way to illustrate the needed interactions between teams to complete a feature.
In this post, I would like to tell you what helped me, how we succeed to engage all the skills needed to develop a product. We'll talk about agility, about lean, about problems.
## Creating a visual feature flow
### Define the steps
## It always comes to **showing** ## It always comes to **showing**
@@ -17,7 +25,7 @@ In this post, I would like to tell you what helped me, how we succeed to engage
## Showing what next the team has to do ## Showing what next the team has to do
Talking is hard, comprehension between two people is hard. How many times a developper came back to the product owner to ask a few questions left in his ticket? How many times a designer was asked to update a design to see how the app will react in case of an edge case? Talking is hard, comprehension between two people is hard. How many times a developper came back to the product owner to ask a few questions left in his ticket? How many times a designer was asked to update a design to see how the app will react for a specific edge case?
Dear developers, how clear is it for you the way you build an app? Dear developers, how clear is it for you the way you build an app?
@@ -36,4 +44,4 @@ May I ask you some questions?
First, can you name your different step before having your app in production? First, can you name your different step before having your app in production?
<ProductionFlow client:visible /> <production-flow client:visible />

View File

@@ -0,0 +1,120 @@
---
title: Introduction to smart notes, how to take notes efficiently
illustration: https://res.cloudinary.com/practicaldev/image/fetch/s--F-oPy0uT--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s8t5g0o0h4x1ss6anr02.png
layout: post
date: 2021-12-16
---
# Introduction to smart notes, how to take notes efficiently
If you are like me, then you struggled to take meaningful notes and usually give up on writing anything down. Still, deep down, you're looking for a way to write efficiently.
## Why do you still have to write notes?
How frustrating is it to read a book or get a new idea during a meeting, and a few days later realize you already forgot about it? You are not alone.
### Our brains are not reliable
#### The limits of our short-term memory
Learning new things could be summarized as the process of linking items that are in our short-term memory to our long-term memory, by far more efficient and reliable. Short-term memory is really convenient to retrieve information quickly. Unfortunately, our short-term memory is extremely limited: it's quickly crowded—[4 (± 1) blocks of information at a time](https://www.cambridge.org/core/journals/behavioral-and-brain-sciences/article/magical-number-4-in-shortterm-memory-a-reconsideration-of-mental-storage-capacity/44023F1147D4A1D44BDC0AD226838496)—and very short-lived. It isnt suitable for complex reflection.
![short-term and long-term memories](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2g5gf0cspgd1mvkxaskm.png)
To make it even worse, the following curve shows how information is quickly lost over time when there is no attempt to retain it. Thats why we tend to retrieve so little information from a book read months ago. It's called the forgetting curve.
![the forgetting curve](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d02a5yl5c20g8aat7rgk.png)
In a nutshell, our brain is limited and quickly forgets the information it's been exposed to.
#### Our cognitive biases
Besides memory, brains aren't reliable because of many cognitive biases: confirmation bias, attentional bias, mere-exposure effect, normalcy bias, you name it. Even if it's the most complex system in our body, our brain must face many obstacles in order to not fool itself. For instance, more often than necessary, we look for arguments that unconsciously reinforce our original thoughts, we tend to argue with a pre-existing belief. This is what Julia Galef calls the "soldier mindset" in "[The Scout Mindset](https://juliagalef.com)". She opposes it to the scout mindset: a mindset in which the search for the truth is what primarily guides its reasoning.
As the scout's goal is to draw the most accurate map in the battlefield, taking notes becomes the map for your thoughts: they tell you what you know, what you don't know and guide you through your reflection.
## The concept of *smart notes*
### Where do smart notes come from?
Smart notes is a term used in the book "[How To Take Smart Notes](https://takesmartnotes.com)" written by Sönke Ahrens that tells the story of Niklas Luhmann, a sociologist who has published hundreds of articles thanks to his note-taking system: [the Zettelkasten method](https://zettelkasten.de/posts/overview).
### Creating the system
Taking notes tends to mean "jotting down ideas on paper during meetings". But we rarely use them afterwards. Notes used once or twice are no good, we cant take advantage of them. Note-taking without structure is pointless. Thats why we have to create a system where each note helps the system snowball effect.
We want a network of permanent notes. The goal is to write permanent notes and to densely connect them together. But to create notes you're proud of, you must iterate a few times with other kinds of notes. Overall, we can look at 3 kinds of notes:
- fleeting notes,
- literature notes,
- permanent notes.
#### Fleeting notes
*Fleeting notes* are your day-to-day notes. When an idea pops into your head or you find an idea interesting in a meeting, you want to write it to free your mental load. They are primarily the pool of new concepts.
Then comes the refinement of the notes. For this, you need to instigate a routine: clearing and deleting non-relevant paragraphs, extracting the ideas into concise notes. This routine is key to accumulate knowledge.
I suggest getting a note per day and starting the routine at a small scale: create a fleeting note each day but refine them only once a week.
#### Literature notes
*Literature notes* are the notes you take on the fly when you are reading a book, a blog post or listening to a podcast. With a pen and a paper next to you, you write with your own words your understanding of the read. The key feature here is that we want a much more active lecture and information consumption than just passive reading/listening. It may seem hard at first but once you incorporate this new habit, you feel almost disappointed reading without extracting what seems compelling.
#### Permanent notes
*Permanent notes* are atomic and connected. A permanent note is about one and only one concept, as simple as possible and connected with others. They come from the selected thoughts chosen from your fleeting and literature notes. They are the backbone of the system so you want to be extremely selective about what will become a good durable note.
When creating a permanent note, scan your previous notes and find connections with the new one. Its the rule: the only way a permanent note can integrate the system is from connections.
### Improve yourself step by step
We want the system to work like our brain: simple ideas linked together as a network of thoughts. We want to start small, one note at a time.
Your notes don't need to be perfect straight away, in fact, they will never be. Because writing is hard.
Actually, writing is a multiple-tasking process:
- writing ideas on the go,
- extracting relevant information,
- criticizing,
- making the writing shorter and the concept clearer,
- rewriting and rewriting again…
We want to separate these different tasks as much as possible because they can inhibit each other. When you write, dont be afraid of being weird or dumb, there will be a time for self-critic. For now just write as if you were teaching it to your younger self.
It might feel uncomfortable.
![Is it me or is a 10 year old child writing this?](https://lh3.googleusercontent.com/T3W3kwKhU1lHsTouBixHVWog1DPju6HMaPp_UrSvA6jgWh_Cuii1I-cYbajvYu_DNf4ZLOH0ZRygyhd1x87u8blVhFD9JtSa3IcF3_7zUAcbrjBHElEnPzSQG5XBqH1KcJdfFAMS)
"Is it me or is a 10 year old child writing this?". Still the more you iterate, the better your notes become, I promise. 😊
### Smart notes are your first feedback loop
This is where it gets exciting. Since you know you will be able to review your notes by yourself, you become better and better at writing and therefore your way of thinking. Notes are the medium between your past, present and future self. Plus, if you become comfortable with your own critique, you'll find yourself accepting even more the challenges of others as you simply want to improve your system.
### How smart notes benefit me
Ive been using this system for the past year and so far it works!
Here is a non-exhaustive list of things I like about it:
- 📝 I can write a blog post—like this onejust by picking ideas from my own notes: no more blank page syndrome,
- 🧠 trusting one and only one system allows the brain to truly free itself from mental load: you know you can go back to your thoughts and ideas later,
- 🕸 making connections between work, hobbies and entertainment create new ideas in a way I didn't expect, I feel more creative!
- 📈 as I said earlier, Im much more incline to face challenges from other people and ask colleagues how I can improve my work,
- 🪴 watching your network grow is rewarding! Its like taking care of your own little garden.
As great as it is, I want to warn you about a few pitfalls into which you could be tempted to fall:
- [collecting](https://zettelkasten.de/posts/collectors-fallacy) only for the sake of collecting isnt helpful. [We dont need it.](https://observer.com/2017/05/the-collectors-fallacy-why-we-gather-things-we-dont-need)
- creating a system per project: we lose the essence of the system; we want to be able to connect notes that don't seem connected at first sight.
- thinking too much of the tree structure for your notes in your taking note app: it is not that important, we just want to look for notes easily,
- copying and pasting what we find on the internet: link the page you want to reference but if the notes are not your own, the system becomes irrelevant (and I quote [CGP Grey](https://youtu.be/wf2VxeIm1no?t=493): *"If your mind is forever filled with the voice of others, how do you know what you think about anything?"*).
With all this in mind, I made [a personal slip-box](https://litenote.space/lite-note/example) I'll be completing over time to let you explore concrete examples!
## Introducing smart notes for others
Im currently wondering if there are other potential applications to the Zettelkasten method other than using it ourselves. At BAM, I work as a team leader and I constantly ask myself how we can improve our way of communication through time in projects. Just like [visual management](https://dev.to/jcalixte/how-does-visual-management-improve-team-efficiency-4kgd), good documentation is essential for a project to succeed. Having clear standards connected to more detailed concepts is the ultimate documentation! But I havent fully experienced it yet.

View File

@@ -0,0 +1,77 @@
---
title: Developers, make your environment loopy 🔁
illustration: https://res.cloudinary.com/practicaldev/image/fetch/s--ydCtwTyU--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ui6knf741v61hk66r9p.png
layout: post
date: 2022-03-13
---
# Developers, make your environment loopy 🔁
When we work, we want feedback: do we do the expected job? Does it fail? Where does it fail? Therefore, we want to receive feedback loops from our environment that gives us useful information about how well we're doing. This is what *loopy* means.
Hi! My name is Julien and I'm a tech leader at [BAM](https://www.bam.tech/bam-agence-experte-design-et-d%C3%A9veloppement-mobile) where we design and develop mobile apps. A part of my job is to provide a healthy environment for my fellow developers to work at their best, improve quicker and then become tech leaders themselves.
In a dynamic system we want to adapt from visible inputs to concrete actions. And to do that, we need standards.
## It starts with standards
Standards are established rules in the team. They are our best guess about what a good job is. Writing and comparing with real life allow us to distinguish what is normal from what is not.
Some tips when writing down a standard, make sure you have:
1. why it's important to write it down,
2. what are its key points,
3. what are the common mistakes we can avoid in the first place,
4. and what are the concrete examples to better understand it.
With these key points in place, we'll be able to compare with the real world and adapt. In a nutshell, to be loopy.
## Loopy code
Once we agree as a team to use a set of standards, we want our tools to know them. Take linters in your code for instance. Linters are scripts that find and sometimes can even fix problems automatically. They are great to provide warnings when a developer diverges from the expected code. It saves time on training, on reviewing and on debugging.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ssfn63tamuaxusylg5rc.png)
The more we use a tool, the more reliant we want it to be. If in the previous paragraph you thought “Well, in my project linters are a pain in the butt, I hate how they force me to do additional work!”; this is a clear hint you are working for your tools and not the other way around. We don't want this. We want computers to execute repetitive tasks and humans to solve problems. So gather your team and update your linter rules so nobody complains. 🤔
## Loopy tests
Talking about tests. If you want to rush, don't write tests. If you want to go fast and go far, write tests.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijjbysabl79j3y1hu5aq.png)
Mental loads are real and a heavy burden. When we start automating tests of some of our code, we can discharge that cognitive load. This will lead to a freer mind and easier and more effective days.
## Loopy production flow
I wrote [an article](https://dev.to/jcalixte/how-does-visual-management-improve-team-efficiency-4kgd) about how useful it is to see the steps in the production flow. By showing the production flow, we know where we are and how good we are. We can then adapt our work/prioritization.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ywgpssrfv7oxeqa0s7fz.png)
We then can spot and identify problems and solve them - they are called “red bins” in lean production. Displaying the problems in front of the whole team helps to highlight how they are slowing the whole process down. That way they can't be ignored and actions must be taken.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d6w9oj12cxsr3n19z08r.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/82ao3k9i4zhq5py6vufq.png)
## Loopy user satisfaction
The better we understand user needs, the clever we can work. There is no shame about being wrong in the first place. The most important thing is to acknowledge and adapt.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/30uudql0ytaocdm5g8hc.png)
## Make your tools work for you not against you
We tend to believe that a part of our job is to use specific tools and deal with its disadvantages because "we're used to it and it's always been that way". We tell ourselves: this is my job and if I fail, it'll be on me. Of course it's a bit more complex than that. In our day to day work, we all make mistakes: these human errors are not a cause but a symptom of a deeper problem in your system. Understanding how and what causes a human error to arise is the beginning of understanding a little bit about a complex system. And in an overly complex system, being able to adapt with feedback loops is better than being able to predict.
## Shorten the loops
Your time is valuable, your focus is valuable, this is all about you and enhancing your work. The quicker you get feedback, the faster you can adapt. And the more feedback you get the more you work smarter.
<center>
*We shape our tools then the tools shape us.*
</center>
___
This post was inspired by the [Loopy tool](https://ncase.me/loopy) from [Nicky Case](https://ncase.me), I can only suggest you take a look!