mirror of
https://github.com/morten-olsen/morten-olsen.github.io.git
synced 2026-02-08 01:46:28 +01:00
Compare commits
10 Commits
v1.1175332
...
v1.1251269
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df713e8e3f | ||
|
|
0e371968ba | ||
|
|
695b69c225 | ||
|
|
5467979563 | ||
|
|
42952604e3 | ||
|
|
4606cbec9c | ||
|
|
52b2572949 | ||
|
|
5f3017c7cf | ||
|
|
84f4de421f | ||
|
|
337cc4e71b |
2
.github/workflows/build-latex.yml
vendored
2
.github/workflows/build-latex.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
node-version: '12'
|
||||
|
||||
- name: Build READM.md
|
||||
run: node readme/index.js > README.md
|
||||
run: node scripts/update-readme.js > README.md
|
||||
|
||||
- uses: EndBug/add-and-commit@v7
|
||||
with:
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -2,3 +2,8 @@
|
||||
/*.log
|
||||
/out
|
||||
/.next
|
||||
/latex/**/*.aux
|
||||
/latex/**/*.log
|
||||
/latex/**/*.in
|
||||
/latex/**/*.pdf
|
||||
/latex/_markdown_*
|
||||
|
||||
174
README.md
Normal file
174
README.md
Normal file
@@ -0,0 +1,174 @@
|
||||
|
||||
|
||||
# Curriculum Vitae
|
||||
|
||||
**[Download the latest version](https://github.com/morten-olsen/curriculum-vitae/releases/latest)**
|
||||
|
||||
|
||||
## Basic Info
|
||||
* **E-mail**: hello@buy-me.coffee
|
||||
* **Location**: Copenhagen, DK
|
||||
* **Github**: https://github.com/morten-olsen
|
||||
* **Homepage**: https://mortenolsen.pro
|
||||
|
||||
------
|
||||
|
||||
## Who am I?
|
||||
|
||||
Hell bend on a conquest to take over the digital world (and the physical, should the chance arise). Am I over ambitious? Perhaps, but with a proven track record in most aspects of things which can process 1s and 0s and a mind which runs at a speed which can battle a well-trained racehorse, I believe I am equipt to undertake this voyage!
|
||||
|
||||
------
|
||||
|
||||
## Platforms and Languages
|
||||
|
||||
Platforms and languages which I have worked with. The list is a shortened down version.
|
||||
|
||||
* **C\#**: 5
|
||||
* **JavaScript**: 5
|
||||
* **NodeJS**: 5
|
||||
* **React Native**: 5
|
||||
* **HTML**: 5
|
||||
* **CSS**: 5
|
||||
* **TypeScript**: 5
|
||||
* **Docker**: 4
|
||||
* **Linux**: 4
|
||||
|
||||
------
|
||||
|
||||
## Experiences
|
||||
|
||||
|
||||
### [Sampension](https://www.sampension.dk)
|
||||
**Senior Frontend Developer** _(2018 - Present)_
|
||||
|
||||
Sampension is a danish pension fund and my work has been to design and help to build a frontend architecture that would run natively on iOS and Android as well as on the web on both desktop and mobile devices.
|
||||
|
||||
It was important to ensure that the project felt at home on all platforms and that it was maintainable by a small team of developers.
|
||||
|
||||
To achieve this we used React Native and React Native for Web to create a unified codebase for all platforms, as well as create a component library which would deal with ensuring the best UX on all platforms.
|
||||
|
||||
|
||||
### [Trendsales](https://www.trendsales.dk)
|
||||
**Frontend Technical Lead** _(2016 - 2018)_
|
||||
|
||||
In 2015 Trendsales decided to build an entirely new platform. It became my responsibility to create a modernized frontend architecture. The work began in 2016 with just me on the project and consisted of a proof of concept version containing everything from framework selection, structure, style guides build chain, continuous deployment, and an actual initial working version. The result where the platform which I was given technical ownership over and which I, along with two others, worked on expanding over the next year. The platform is currently powering _m.trendsales.dk_. The project is build using React and state management are done using Redux. In addition to the of the shelve frameworks, we also needed to develop quite a few bespoke frameworks, in order to meet demands. Among others, these were created to solve the following issues:
|
||||
|
||||
* Introducing a new navigational paradigm
|
||||
* Create a more flexible routing mechanism
|
||||
* Be able to serve skeleton page, for page transitions while still being able to create complete server-side pages
|
||||
* Ensure project flows between multiple systems such as Github, Jira, Octopus Deploy, AppVeyor and Docker
|
||||
|
||||
|
||||
### [Trendsales](https://www.trendsales.dk)
|
||||
**iOS and Android Developer** _(2012 - 2015)_
|
||||
|
||||
I became responsible for the iOS platform, which was a task that required a new app to be built from the ground up using _Xamarin_[^1]. In addition to that, a new API to support the app along with support for our larger vendors was needed which had to be build using something closely similar to _Microsoft MVC_ so that other people could join the project at a later stage.
|
||||
|
||||
he project started in October with the initial version available to our users in late December.
|
||||
|
||||
This project represented my first adventure into mobile development and became an app with more than 15 million screen views and 1.5 million sessions per month.
|
||||
|
||||
After that, I joined two other colleagues, who were working on an Android version of the app, to form a join mobile development team.
|
||||
|
||||
Throughout the period I also worked on the backend for the web page from time to time.
|
||||
|
||||
[^1]: Called MonoTouch at the time
|
||||
|
||||
|
||||
### [Trendsales](https://www.trendsales.dk)
|
||||
**Web Developer** _(2012 - 2012)_
|
||||
|
||||
I got a part-time job at Trendsales, where my primary responsibility was maintaining the API which powered the iOS app. Quickly my tasks became more diverse, and I ended using about 25-50% of my time on the API, while the remaining was spend doing work on the platform in general.
|
||||
|
||||
|
||||
### [BilZonen](http://www.bilzonen.dk)
|
||||
**Web Developer** _(2010 - 2012)_
|
||||
|
||||
I work as a part-time web developer on bilzonen.dk. I have worked with both day-to-day maintenance and large scale projects (new search module, integration of new data catalog, mobile site, new-car-catalog and the entire dealer solution). The page is an Umbraco solution, with all .NET (C#) code. I have introduced a new custom build provider-model system, which allows data-providers to move data between data stores, external services, and the site. (search, caching and external car date is running through the provider system). Also, i have set up the development environment, from setting up virtual server hosts to building custom tool for building and unit testing.
|
||||
|
||||
|
||||
### [Haastrup IT](undefined)
|
||||
**Web Developer** _(2009 - 2010)_
|
||||
|
||||
I have worked as a part time project koordinator and systems developer, sitting with responsibility for a wide variety of projects including projects for "Københavns Kommune" (Navision reporting software) and "Syddanmarks kommune" (Electronic application processing system). Most projects were made in C#, but also PHP, VB, ActionScript. In addtion to that i maintained the in-house hosting setup.
|
||||
|
||||
|
||||
### [Sydbank](undefined)
|
||||
**IT Hotline** _(2007 - 2009)_
|
||||
|
||||
I work as a part-time supporter of customers (private and business) and staff, on Sydbanks different electronic banking systems. Mostly telephonic bug finding and PC setup.
|
||||
|
||||
|
||||
|
||||
------
|
||||
|
||||
## Frameworks
|
||||
|
||||
This list of frameworks is a curated list of frameworks I have been using recently and therefore are all amongst frameworks which I prefer to work with.
|
||||
|
||||
* **React**: 5
|
||||
* **Redux**: 5
|
||||
* **Webpack**: 5
|
||||
* **RxJS**: 3
|
||||
* **RxDB**: 5
|
||||
* **Styled-Components**: 5
|
||||
* **GraphQL**: 3
|
||||
* **Babel**: 5
|
||||
|
||||
------
|
||||
|
||||
## Projects of interest
|
||||
|
||||
A list of projects I have worked with or am working on, for which the source is publicly available. Keep in mind that some of these projects are in early stages, and some are merely created for the training, and may not represent the actual way I think production code should be structured
|
||||
|
||||
|
||||
**[Todoma](https://github.com/morten-olsen/todoma)**
|
||||
_GTD meets kanban in this mobile and web task manager_
|
||||
|
||||
A task manager that merges GTD and Kanban into a system that I use for organising my day. The app is written in React Native and runs both on iOS, Android and web, all with the same codebase
|
||||
|
||||
|
||||
|
||||
**[Curriculum Vitae](https://github.com/morten-olsen/curriculum-vitae)**
|
||||
_Automated CV building_
|
||||
|
||||
Another small fun project is this resume, as it is created in \LaTeX, versioned using _Git_ on _Github_ and set to automatically build and create multiple release versions using _Travis CI_ and _Docker
|
||||
|
||||
|
||||
|
||||
**[React Native Debug Console](https://github.com/morten-olsen/react-native-debug-console)**
|
||||
_Easily debug in-the-wild React Native apps_
|
||||
|
||||
I needed a tool that allowed me to easily debug the JavaScript and network asspect of a React Native app after it has been push to testers. This tool adds a development menu inspired by Chrome Dev Tools to a React Native app, allowing for an easier debugging experience
|
||||
|
||||
|
||||
|
||||
**[IoT (Name TBD)](https://https://iot.mortenolsen.pro)**
|
||||
_Developer focused IoT platform_
|
||||
|
||||
A lot of IoT platforms tries to be user friendly, sacrificing customizability and still staying to complicated for the general user. This project creates a developer focus that does not trade control for user friendliness, retaining focus on 'coding' your own home setup
|
||||
|
||||
|
||||
|
||||
**[Redux App State](https://github.com/trendsales/redux-app-state)**
|
||||
_Advanced app like navigation on the web_
|
||||
|
||||
One of the most significant issues I had to overcome when I began building the revamped Trendsales front end was that the design called for an advanced navigation strategy, which did not fit into the webs linear document structure, but rather a branched multi-layer navigation. Therefore I created this project to create a high-level abstraction using a navigation trap to allow for much more advanced navigation patterns
|
||||
|
||||
|
||||
|
||||
**[Clubhouse Protocol](https://github.com/clubhouse-protocol)**
|
||||
_An OpenPGP based crypto group communication with decentralised rule management for both humans and machines_
|
||||
|
||||
A cryptographic communication platform where all messages are end-to-end encrypted. It uses a decentralized rule set, so every message is verified and applied by the clients individually
|
||||
|
||||
|
||||
|
||||
**[wolfsquad.co](https://github.com/morten-olsen/wolfsquad)**
|
||||
_A fun experiment with encryption_
|
||||
|
||||
This is an old project I did to play around with encryption in JavaScript. The initial boot loader is served as regular JavaScript, but after login, all communication has a level of encryption applied based upon the password, which is never sent to the server, as its encryption abilities offer this feature, without having to transfer sensitive information. It is currently available at https://wolfsquad.co/ with the username _admin_ and the password _password_
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,131 +1,103 @@
|
||||
// https://redstapler.co/cool-nebula-background-effect-three-js/
|
||||
|
||||
// https://redstapler.co/cool-nebula-background-effect-three-js/
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
interface CloudProps {
|
||||
texture: THREE.Texture;
|
||||
position: [number, number, number];
|
||||
rotation: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
const setup = () => {
|
||||
let scene, camera, renderer;
|
||||
let cloudParticles = [];
|
||||
|
||||
function init() {
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(60,window.innerWidth / window.innerHeight,1,1000);
|
||||
camera.position.z = 1;
|
||||
camera.rotation.x = 1.16;
|
||||
camera.rotation.y = -0.12;
|
||||
camera.rotation.z = 0.27;
|
||||
let ambient = new THREE.AmbientLight(0x555555);
|
||||
scene.add(ambient);
|
||||
renderer = new THREE.WebGLRenderer();
|
||||
renderer.setSize(window.innerWidth,window.innerHeight);
|
||||
scene.fog = new THREE.FogExp2(0x03544e, 0.001);
|
||||
renderer.setClearColor(scene.fog.color);
|
||||
renderer.domElement.style.position = 'fixed';
|
||||
renderer.domElement.style.top = '0';
|
||||
renderer.domElement.style.left = '0';
|
||||
renderer.domElement.style.width = '100%';
|
||||
renderer.domElement.style.height = '100%';
|
||||
renderer.domElement.style.zIndex = -1;
|
||||
renderer.domElement.style.animationName = 'fadein';
|
||||
renderer.domElement.style.animationDuration = '2s';
|
||||
|
||||
document.body.appendChild(renderer.domElement);
|
||||
addParticles();
|
||||
addLights();
|
||||
render();
|
||||
requestAnimationFrame(() => {
|
||||
//renderer.domElement.style.opacity = 1;
|
||||
})
|
||||
}
|
||||
|
||||
const initialClouds = new Array(30).fill(undefined).map((_, i) => ({
|
||||
name: `id_${i}`,
|
||||
position: [
|
||||
const addParticles = () => {
|
||||
let loader = new THREE.TextureLoader();
|
||||
loader.load("/images/smoke.png", (texture) => {
|
||||
const cloudGeo = new THREE.PlaneBufferGeometry(500,500);
|
||||
const cloudMaterial = new THREE.MeshLambertMaterial({
|
||||
map:texture,
|
||||
transparent: true
|
||||
});
|
||||
for(let p=0; p<50; p++) {
|
||||
let cloud = new THREE.Mesh(cloudGeo, cloudMaterial);
|
||||
cloud.position.set(
|
||||
Math.random()*800 -400,
|
||||
500,
|
||||
Math.random() * 500 - 500,
|
||||
] as [number, number, number],
|
||||
rotation: {
|
||||
x: 1.16,
|
||||
y: -0.12,
|
||||
z: Math.random() * 2 * Math.PI,
|
||||
},
|
||||
}));
|
||||
|
||||
const Cloud: React.FC<CloudProps> = ({ texture, position, rotation }) => {
|
||||
return (
|
||||
<mesh position={position} rotation={[ rotation.x, rotation.y, rotation.z ]}>
|
||||
<planeBufferGeometry args={[500, 500]} />
|
||||
<meshLambertMaterial opacity={.5} map={texture} transparent={true} />
|
||||
</mesh>
|
||||
Math.random()*500-500
|
||||
);
|
||||
cloud.rotation.x = 1.16;
|
||||
cloud.rotation.y = -0.12;
|
||||
cloud.rotation.z = Math.random()*2*Math.PI;
|
||||
cloud.material.opacity = 0.55;
|
||||
cloudParticles.push(cloud);
|
||||
scene.add(cloud);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const addLights = () => {
|
||||
let directionalLight = new THREE.DirectionalLight(0xff8c19);
|
||||
directionalLight.position.set(0,0,1);
|
||||
scene.add(directionalLight);
|
||||
|
||||
let orangeLight = new THREE.PointLight(0xcc6600,50,450,1.7);
|
||||
orangeLight.position.set(200,300,100);
|
||||
scene.add(orangeLight);
|
||||
let redLight = new THREE.PointLight(0xd8547e,50,450,1.7);
|
||||
redLight.position.set(100,300,100);
|
||||
scene.add(redLight);
|
||||
let blueLight = new THREE.PointLight(0x3677ac,50,450,1.7);
|
||||
blueLight.position.set(300,300,200);
|
||||
scene.add(blueLight);
|
||||
};
|
||||
|
||||
const Clouds: React.FC<{}> = () => {
|
||||
const { camera } = useThree();
|
||||
const loader = useMemo(() => new THREE.TextureLoader(), []);
|
||||
const [texture, setTexture] = useState<THREE.Texture | undefined>();
|
||||
const [clouds, setClouds] = useState(initialClouds);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(camera);
|
||||
camera.rotateX(1.16);
|
||||
camera.rotateY(-0.12);
|
||||
camera.rotateZ(0.27);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loader.load('images/smoke.png', (loadedTexture) => {
|
||||
setTexture(loadedTexture);
|
||||
function render() {
|
||||
cloudParticles.forEach(p => {
|
||||
p.rotation.z -=0.001;
|
||||
});
|
||||
}, []);
|
||||
|
||||
useFrame(() => {
|
||||
setClouds(current => current.map(c => ({
|
||||
...c,
|
||||
rotation: {
|
||||
...c.rotation,
|
||||
z: c.rotation.z -= 0.001,
|
||||
}
|
||||
})));
|
||||
});
|
||||
|
||||
if (!texture) {
|
||||
return <></>;
|
||||
renderer.render(scene,camera);
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{clouds.map((cloud) => (
|
||||
<Cloud
|
||||
key={cloud.name}
|
||||
texture={texture}
|
||||
position={cloud.position}
|
||||
rotation={cloud.rotation}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const style = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
top: 0,
|
||||
left: 0,
|
||||
position: 'fixed' as any,
|
||||
zIndex: -1,
|
||||
}
|
||||
init();
|
||||
};
|
||||
|
||||
const Background: React.FC<{}> = () => {
|
||||
const fog = useMemo(() => new THREE.FogExp2(0x03544e, 0.001), []);
|
||||
const [aspect, setAspect] = useState(global.window ? window.innerWidth / window.innerHeight : 1);
|
||||
if (!global.window) {
|
||||
return <Canvas style={style}><></></Canvas>
|
||||
}
|
||||
useEffect(() => {
|
||||
const update = () => {
|
||||
setAspect(window.innerWidth / window.innerHeight);
|
||||
};
|
||||
window.addEventListener('resize', update);
|
||||
return () => {
|
||||
window.removeEventListener('resize', update);
|
||||
}
|
||||
setup();
|
||||
}, []);
|
||||
return (
|
||||
<Canvas style={style}>
|
||||
<color attach="background" args={[0x03544e]} />
|
||||
<scene fog={fog}>
|
||||
<perspectiveCamera
|
||||
args={[60, aspect, 1, 1000]}
|
||||
aspect={aspect}
|
||||
position={[0, 0, 0]}
|
||||
/>
|
||||
<ambientLight args={[0x555555]} />
|
||||
<directionalLight args={[0xff8c19]} position={[0,0,1]} />
|
||||
<pointLight args={[0xcc6600, 50, 450, 1.7]} position={[200,300,100]} />
|
||||
<pointLight args={[0xd8547e, 50, 450, 1.7]} position={[100,300,100]} />
|
||||
<pointLight args={[0x3677ac, 50, 450, 1.7]} position={[300,300,200]} />
|
||||
<Clouds />
|
||||
</scene>
|
||||
</Canvas>
|
||||
);
|
||||
return <></>
|
||||
};
|
||||
|
||||
export default Background;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import Image from 'next/image'
|
||||
import image from '../public/images/me.jpg';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
@@ -11,14 +10,16 @@ const Wrapper = styled.div`
|
||||
padding: 80px;
|
||||
`;
|
||||
|
||||
const ImageWrapper = styled.div`
|
||||
const ImageWrapper = styled.div<{loaded: boolean}>`
|
||||
border-radius: 50%;
|
||||
border: solid 30px rgba(255, 255, 255, .5);
|
||||
box-shadow: 0 0 35px rgba(255, 255, 255, .5);
|
||||
overflow: hidden;
|
||||
border: solid 10px rgba(255, 255, 255, .5);
|
||||
box-shadow: 0 0 35px rgba(0, 0, 0, .5);
|
||||
width: 100%;
|
||||
max-width: 360px;
|
||||
max-width: 300px;
|
||||
position: relative;
|
||||
transition: all 1.2s;
|
||||
opacity: ${({ loaded }) => loaded ? '1' : '0'};
|
||||
transform: rotateY(180deg) translateZ(0);
|
||||
`;
|
||||
|
||||
const Spacer = styled.div`
|
||||
@@ -35,6 +36,12 @@ const Title = styled.h1`
|
||||
0 0 5px rgba(255, 255, 255, .5);
|
||||
0 0 10px rgba(0, 0, 0, .5);
|
||||
margin-bottom: 0px;
|
||||
letter-spacing: 1px;
|
||||
letter-spacing: 7px;
|
||||
|
||||
&::first-letter {
|
||||
font-size: 36px;
|
||||
}
|
||||
`;
|
||||
|
||||
const SubTitle = styled.h2`
|
||||
@@ -48,25 +55,40 @@ const SubTitle = styled.h2`
|
||||
0 0 10px rgba(0, 0, 0, .5);
|
||||
`;
|
||||
|
||||
const Divider = styled.div`
|
||||
margin-top: 70px;
|
||||
const Image = styled.img<{blurDataURL: string}>`
|
||||
background: url("${({ blurDataURL }) => blurDataURL}");
|
||||
position: absolute;
|
||||
background-size: cover;
|
||||
border-radius: 50%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, .5);
|
||||
box-shadow: 0 0 30px rgba(255, 255, 255, .7);
|
||||
`;
|
||||
height: 100%;
|
||||
`
|
||||
|
||||
const Me: React.FC<{}> = () => (
|
||||
const Me: React.FC<{}> = () => {
|
||||
const imgRef = useRef<HTMLImageElement>();
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
useEffect(() => {
|
||||
if (imgRef.current && imgRef.current.complete) {
|
||||
setLoaded(true);
|
||||
}
|
||||
}, [imgRef]);
|
||||
return (
|
||||
<Wrapper>
|
||||
<ImageWrapper>
|
||||
<Image layout="fill" {...image} />
|
||||
<ImageWrapper loaded={loaded}>
|
||||
<Image
|
||||
ref={imgRef}
|
||||
src={image.src}
|
||||
blurDataURL={image.blurDataURL}
|
||||
onLoad={() => setLoaded(true)}
|
||||
/>
|
||||
<Spacer />
|
||||
</ImageWrapper>
|
||||
<Title>Morten Olsen</Title>
|
||||
<SubTitle>“...One part genius, one part crazy”</SubTitle>
|
||||
<Divider />
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Me;
|
||||
|
||||
@@ -15,11 +15,9 @@ const Outer = styled.div`
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const Image = styled.div<{ src: string }>`
|
||||
const Image = styled.img<{ src: string }>`
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: url('${({ src }) => src}');
|
||||
background-size: cover;
|
||||
margin-right: 10px;
|
||||
transition: all .8s;
|
||||
filter: grayscale(100%) invert();
|
||||
@@ -31,7 +29,7 @@ const Wrapper = styled.div`
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
flex-wrap: wrap;
|
||||
max-width: 1000px;
|
||||
max-width: 1600px;
|
||||
`;
|
||||
|
||||
const ItemWrapper = styled.a`
|
||||
@@ -53,7 +51,7 @@ const ItemWrapper = styled.a`
|
||||
color: #000;
|
||||
box-shadow: 0 0 35px rgba(0, 0, 0, .3);
|
||||
|
||||
&> div {
|
||||
&> img {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
}
|
||||
|
||||
34
data.json
34
data.json
@@ -2,7 +2,7 @@
|
||||
"type": "info",
|
||||
"data": {
|
||||
"name": "Morten Olsen",
|
||||
"image": "assets/me.jpg",
|
||||
"image": "public/images/me.jpg",
|
||||
"info": [{
|
||||
"name": "E-mail",
|
||||
"value": "hello@buy-me.coffee"
|
||||
@@ -12,6 +12,9 @@
|
||||
},{
|
||||
"name": "Github",
|
||||
"value": "https://github.com/morten-olsen"
|
||||
}, {
|
||||
"name": "Homepage",
|
||||
"value": "https://mortenolsen.pro"
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
@@ -66,7 +69,7 @@
|
||||
"title": "Senior Frontend Developer",
|
||||
"startDate": "2018",
|
||||
"endDate": "Present",
|
||||
"description": "Sampension is a danish pension fund and my work has been to design and help to build a frontend architecture that would run natively on iOS and Android as well as on the web on both desktop and mobile devices.\n\nIt was important to ensure that the project felt at home on all platforms and that it was maintainable by a small team of developers.\n\nTo achieve this we used React Native and React Native for Web to create a unified codebase for all platforms, as well as create a component library which would deal with ensuring the best UX on all platforms."
|
||||
"description": "Sampension is a danish pension fund.\nInitially I was hired to help build the frontend and its architecture of their new digital self service product (\"Sampension\" on Apple App Store or Google Play Store). The project was to create a single code base that could handle both iOS, Android and web and be responsive/adaptive to be able to give a great UX on both mobile, tablets as well as desktop computers. The product is created using React Native and React Native Web and currently has around 96% shared code between the different platforms.\n\nAdditionally I have been working on CI/CD and various DevOps related tasks to build up the tooling around the product (AWS S3, AWS Lambda, AWS Cloudfront, Github, Gihub Actions, Azure DevOps, Azure, etc.)\n\nI am also currently working with some initial project analysis for new projects and I have begun maintaining some of our backend code (C#)"
|
||||
}, {
|
||||
"company": {
|
||||
"name": "Trendsales",
|
||||
@@ -152,26 +155,37 @@
|
||||
"level": "5"
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
"type": "text",
|
||||
"data": {
|
||||
"title": "What am I looking for in a job position?",
|
||||
"content": "I am a person who likes a challange, and therefore I also very much expect a company to provide tasks that challange me, and allows me to learn more and ideally from people smarter than me. I prefer the entrepreneurial spirit and to be in a company that does not wait to adjust to the market until the last minute, but one that likes to see what new technologies can do for them. I prefer an informal office tone and a flat team structure (Or atleast flat enough that anyone, regardless of seniority can have their voice heard)."
|
||||
}
|
||||
}, {
|
||||
"type": "projects",
|
||||
"data": {
|
||||
"title": "Projects of interest",
|
||||
"description": "A list of projects I have worked with or am working on, for which the source is publicly available. Keep in mind that some of these projects are in early stages, and some are merely created for the training, and may not represent the actual way I think production code should be structured",
|
||||
"projects": [{
|
||||
"name": "Clash of Robots",
|
||||
"tagline": "AI vs AI game enginge and IDE",
|
||||
"url": "github.com/clash-of-robots/core",
|
||||
"description": "A game engine for creating AI vs. AI turn-based games, including an IDE (still work in progress). The project is built to be the base for a space drone vs. space drone AI game. Players are given control over a set of drones and have to traverse the solar system, fighting to prosper as selles bots, pirate bots, communication relays or whatever the player decides to do within the confines of the sandbox."
|
||||
"name": "Todoma",
|
||||
"tagline": "GTD meets kanban in this mobile and web task manager",
|
||||
"url": "github.com/morten-olsen/todoma",
|
||||
"description": "A task manager that merges GTD and Kanban into a system that I use for organising my day. The app is written in React Native and runs both on iOS, Android and web, all with the same codebase"
|
||||
}, {
|
||||
"name": "Curriculum Vitae",
|
||||
"tagline": "Automated CV building",
|
||||
"url": "github.com/morten-olsen/curriculum-vitae",
|
||||
"description": " Another small fun project is this resume, as it is created in \\LaTeX, versioned using _Git_ on _Github_ and set to automatically build and create multiple release versions using _Travis CI_ and _Docker "
|
||||
}, {
|
||||
"name": "LifeFlow Mind",
|
||||
"tagline": "Evernote meets \\LaTeX meets Jupyter",
|
||||
"url": "github.com/lifeflow-mind",
|
||||
"description": "I love Evernote simple digital note storages, but as a developer and general nerd, its features often come up short. Which is why I am working on a project named Mind, which attempts to bridge the best aspect of Evernote, with a dynamic view system, so new views (for instance a calendar or a contact view) can be introduced. Also, it can render mathematical formulas using \\LaTeX and execute code snippets, to test and present code directly inside documents. A somewhat working demo can be found at https://morten-olsen.github.io/mind"
|
||||
"name": "React Native Debug Console",
|
||||
"tagline": "Easily debug in-the-wild React Native apps",
|
||||
"url": "github.com/morten-olsen/react-native-debug-console",
|
||||
"description": "I needed a tool that allowed me to easily debug the JavaScript and network asspect of a React Native app after it has been push to testers. This tool adds a development menu inspired by Chrome Dev Tools to a React Native app, allowing for an easier debugging experience"
|
||||
}, {
|
||||
"name": "IoT (Name TBD)",
|
||||
"tagline": "Developer focused IoT platform",
|
||||
"url": "https://iot.mortenolsen.pro",
|
||||
"description": "A lot of IoT platforms tries to be user friendly, sacrificing customizability and still staying to complicated for the general user. This project creates a developer focus that does not trade control for user friendliness, retaining focus on 'coding' your own home setup"
|
||||
}, {
|
||||
"name": "Redux App State",
|
||||
"tagline": "Advanced app like navigation on the web",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import Head from 'next/head';
|
||||
import Background from '../components/Background';
|
||||
import Me from '../components/Me';
|
||||
@@ -7,16 +9,42 @@ import htbLogo from '../public/images/logos/htb.svg';
|
||||
import githubLogo from '../public/images/logos/github.svg';
|
||||
import linkedinLogo from '../public/images/logos/linkedin.svg';
|
||||
import stackOverflowLogo from '../public/images/logos/stackoverflow.svg';
|
||||
import codinGameLogo from '../public/images/logos/codingame.svg';
|
||||
import resumeLogo from '../public/images/logos/resume.svg';
|
||||
|
||||
const Globals = createGlobalStyle`
|
||||
html, body, #__next {
|
||||
height: 90%;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
background: #03544e;
|
||||
}
|
||||
|
||||
@keyframes fadein {
|
||||
from {opacity: 0}
|
||||
to {opacity: 1}
|
||||
}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`
|
||||
|
||||
const Frontpage: React.FC<{}> = () => {
|
||||
return (
|
||||
<>
|
||||
<Wrapper>
|
||||
<Head>
|
||||
<title>Morten Olsen</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300&display=swap" rel="stylesheet" />
|
||||
</Head>
|
||||
<Globals />
|
||||
<Background />
|
||||
<Me />
|
||||
<Social
|
||||
@@ -32,13 +60,21 @@ const Frontpage: React.FC<{}> = () => {
|
||||
title: 'Stack Overflow',
|
||||
link: 'https://stackoverflow.com/users/1689055/morten-olsen',
|
||||
logo: stackOverflowLogo,
|
||||
}, {
|
||||
title: 'Codingame',
|
||||
link: 'https://www.codingame.com/profile/8b34b1812baa75715c972bfe190c0aed4552622',
|
||||
logo: codinGameLogo,
|
||||
}, {
|
||||
title: 'Resumè',
|
||||
link: 'https://github.com/morten-olsen/morten-olsen.github.io/releases/',
|
||||
logo: resumeLogo,
|
||||
}, {
|
||||
title: 'Linkedin',
|
||||
link: 'https://www.linkedin.com/in/mortenolsendk',
|
||||
logo: linkedinLogo,
|
||||
}]}
|
||||
/>
|
||||
</>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
1
public/images/logos/codingame.svg
Normal file
1
public/images/logos/codingame.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 2083 2083" xmlns="http://www.w3.org/2000/svg" width="2500" height="2500"><path d="M0 0h2083v2083H0z" fill="transparent"/><g fill="#1f2528"><path d="M0 1636c411.554-97.771 692.897-283.177 724-637 10.714-131.212 67.364-243.777 216-319 207.675-80.617 305.728-52.164 390-10 92.668 81.722 119.468 199.94 50 371.5-31.914 100.266-291.001 223.867-410 258.5-392.539 175.893-595.504 430.282-561 783H0z"/><ellipse cx="1666.5" cy="408" rx="196.5" ry="192"/></g></svg>
|
||||
|
After Width: | Height: | Size: 560 B |
@@ -1,22 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 176 33" style="enable-background:new 0 0 176 33;" xml:space="preserve">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 33 33" style="enable-background:new 0 0 176 33;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#000;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M48.2,22.4v-4.6c0-0.2-0.1-0.3-0.3-0.3h-4c-0.2,0-0.3,0.1-0.3,0.3v4.6c0,0.2-0.1,0.3-0.3,0.3h-2.4 c-0.2,0-0.3-0.1-0.3-0.3V10.6c0-0.2,0.1-0.3,0.3-0.3h2.4c0.2,0,0.3,0.1,0.3,0.3v4.1c0,0.2,0.1,0.3,0.3,0.3h4c0.2,0,0.3-0.1,0.3-0.3 v-4.1c0-0.2,0.1-0.3,0.3-0.3H51c0.2,0,0.3,0.1,0.3,0.3v11.9c0,0.2-0.1,0.3-0.3,0.3h-2.4C48.4,22.7,48.2,22.6,48.2,22.4z"/>
|
||||
<path class="st0" d="M57.9,20.3l-0.7,2.2c0,0.1-0.2,0.2-0.3,0.2h-2.6c-0.2,0-0.3-0.2-0.3-0.4l4-11.9c0-0.1,0.2-0.2,0.3-0.2H62 c0.1,0,0.2,0.1,0.3,0.2l4,11.9c0.1,0.2-0.1,0.4-0.3,0.4h-2.6c-0.1,0-0.2-0.1-0.3-0.2l-0.7-2.2c0-0.1-0.2-0.2-0.3-0.2h-4.1 C58.1,20.1,58,20.2,57.9,20.3z M61.5,16.8c-0.5-1.5-0.9-3-1.3-4.5h0c-0.4,1.5-0.8,3-1.2,4.5l-0.1,0.5c-0.1,0.2,0.1,0.4,0.3,0.4h2.2 c0.2,0,0.3-0.2,0.3-0.4L61.5,16.8z"/>
|
||||
<path class="st0" d="M79.2,14.8h-2.6c-0.1,0-0.3-0.1-0.3-0.2c-0.2-1.2-1-1.9-2.3-1.9c-1.6,0-2.7,1.5-2.7,3.9s1,3.9,2.6,3.9 c1.2,0,2.1-0.7,2.3-2c0-0.1,0.1-0.2,0.3-0.2h2.6c0.2,0,0.3,0.2,0.3,0.3c-0.4,2.9-2.4,4.5-5.5,4.5c-3.6,0-5.8-2.5-5.8-6.5 s2.2-6.5,5.8-6.5c3.1,0,5.1,1.7,5.4,4.4C79.5,14.6,79.4,14.8,79.2,14.8z"/>
|
||||
<path class="st0" d="M93.4,22.7h-3.1c-0.1,0-0.2,0-0.2-0.1L86.3,17h0v5.4c0,0.2-0.1,0.3-0.3,0.3h-2.4c-0.2,0-0.3-0.1-0.3-0.3V10.6 c0-0.2,0.1-0.3,0.3-0.3H86c0.2,0,0.3,0.1,0.3,0.3v5.1h0l3.6-5.2c0.1-0.1,0.1-0.1,0.2-0.1H93c0.2,0,0.4,0.3,0.2,0.5l-4,5.2 c-0.1,0.1-0.1,0.2,0,0.4l4.4,5.9C93.7,22.4,93.6,22.7,93.4,22.7z"/>
|
||||
<path class="st0" d="M101.8,12v10.4c0,0.2-0.1,0.3-0.3,0.3h-1c-0.2,0-0.3-0.1-0.3-0.3V12c0-0.2-0.1-0.3-0.3-0.3h-3.7 c-0.2,0-0.3-0.1-0.3-0.3v-0.9c0-0.2,0.1-0.3,0.3-0.3h9.5c0.2,0,0.3,0.1,0.3,0.3v0.9c0,0.2-0.1,0.3-0.3,0.3h-3.7 C101.9,11.7,101.8,11.8,101.8,12z"/>
|
||||
<path class="st0" d="M117.3,22.4v-5.2c0-0.2-0.1-0.3-0.3-0.3h-5.8c-0.2,0-0.3,0.1-0.3,0.3v5.2c0,0.2-0.1,0.3-0.3,0.3h-1 c-0.2,0-0.3-0.1-0.3-0.3V10.6c0-0.2,0.1-0.3,0.3-0.3h1c0.2,0,0.3,0.1,0.3,0.3v4.7c0,0.2,0.1,0.3,0.3,0.3h5.8c0.2,0,0.3-0.1,0.3-0.3 v-4.7c0-0.2,0.1-0.3,0.3-0.3h1c0.2,0,0.3,0.1,0.3,0.3v11.9c0,0.2-0.1,0.3-0.3,0.3h-1C117.4,22.7,117.3,22.6,117.3,22.4z"/>
|
||||
<path class="st0" d="M125.5,12v3.3c0,0.2,0.1,0.3,0.3,0.3h5.5c0.2,0,0.3,0.1,0.3,0.3v0.8c0,0.2-0.1,0.3-0.3,0.3h-5.5 c-0.2,0-0.3,0.1-0.3,0.3V21c0,0.2,0.1,0.3,0.3,0.3h6c0.2,0,0.3,0.1,0.3,0.3v0.9c0,0.2-0.1,0.3-0.3,0.3h-7.6c-0.2,0-0.3-0.1-0.3-0.3 V10.6c0-0.2,0.1-0.3,0.3-0.3h7.4c0.2,0,0.3,0.1,0.3,0.3v0.9c0,0.2-0.1,0.3-0.3,0.3h-5.8C125.7,11.7,125.5,11.8,125.5,12z"/>
|
||||
<path class="st0" d="M141,10.3c3.1,0,4.5,1.3,4.5,3.2c0,1.5-0.7,2.3-2.1,2.8v0c1.6,0.4,2.4,1.3,2.4,2.9c0,2.1-1.7,3.5-4.5,3.5h-4.9 c-0.2,0-0.3-0.1-0.3-0.3V10.6c0-0.2,0.1-0.3,0.3-0.3H141z M139.1,15c0,0.2,0.1,0.3,0.3,0.3h1.3c1.3,0,1.9-0.4,1.9-1.4 s-0.6-1.3-1.8-1.3h-1.4c-0.2,0-0.3,0.1-0.3,0.3V15z M139.1,20.1c0,0.2,0.1,0.3,0.3,0.3h1.4c1.4,0,2-0.4,2-1.4c0-1-0.6-1.5-2-1.5 h-1.3c-0.2,0-0.3,0.1-0.3,0.3V20.1z"/>
|
||||
<path class="st0" d="M160.8,16.5c0,4-2.3,6.5-6,6.5c-3.7,0-6-2.5-6-6.5s2.3-6.5,6-6.5C158.4,10,160.8,12.5,160.8,16.5z M151.9,16.5 c0,2.4,1,3.9,2.9,3.9c1.8,0,2.8-1.4,2.8-3.9s-1-3.9-2.8-3.9C152.9,12.6,151.9,14.1,151.9,16.5z"/>
|
||||
<path class="st0" d="M173.4,22.7h-2.9c-0.1,0-0.2-0.1-0.3-0.2l-2.2-4.1h0l-2.2,4.1c-0.1,0.1-0.1,0.2-0.3,0.2h-2.9 c-0.2,0-0.4-0.2-0.2-0.4l3.5-5.8c0.1-0.1,0.1-0.2,0-0.3l-3.3-5.5c-0.1-0.2,0-0.4,0.2-0.4h2.8c0.1,0,0.2,0.1,0.3,0.2l2,4h0l2-4 c0-0.1,0.2-0.2,0.3-0.2h2.8c0.2,0,0.4,0.2,0.2,0.4l-3.3,5.5c-0.1,0.1-0.1,0.2,0,0.3l3.5,5.8C173.8,22.5,173.7,22.7,173.4,22.7z"/>
|
||||
</g>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st1" d="M28.6,9.3C28.6,9.3,28.6,9.3,28.6,9.3c0-0.3-0.1-0.6-0.4-0.8c0,0,0,0,0,0c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1-0.1 c0,0,0,0,0,0L15.6,1.2c0,0-0.1,0-0.1,0C15.3,1,15.1,1,14.9,1c-0.1,0-0.2,0-0.3,0.1c-0.1,0-0.1,0.1-0.2,0.1L2,8.3c0,0,0,0,0,0 c0,0,0,0,0,0c0,0,0,0,0,0C1.8,8.4,1.7,8.5,1.7,8.6c0,0,0,0,0,0C1.5,8.8,1.4,9,1.4,9.3c0,0,0,0,0,0c0,0,0,0,0,0v14.3 c0,0.4,0.2,0.8,0.6,1l12.4,7.2c0,0,0,0,0.1,0c0,0,0,0,0,0c0.1,0,0.1,0.1,0.2,0.1c0,0,0,0,0,0c0.1,0,0.2,0,0.2,0s0.2,0,0.2,0 c0,0,0,0,0,0c0.1,0,0.1,0,0.2-0.1c0,0,0,0,0,0c0,0,0,0,0.1,0l12.4-7.2c0.4-0.2,0.6-0.6,0.6-1L28.6,9.3C28.6,9.4,28.6,9.3,28.6,9.3z M6.3,8.9L14.7,4c0.2-0.1,0.4-0.1,0.5,0l8.4,4.9c0.4,0.2,0.4,0.7,0,0.9l-8.4,4.9c-0.2,0.1-0.4,0.1-0.5,0L6.3,9.8 C5.9,9.6,5.9,9.1,6.3,8.9z M13.5,27.4c0,0.4-0.4,0.7-0.8,0.5L4.3,23C4.1,22.9,4,22.7,4,22.5v-9.7c0-0.4,0.4-0.7,0.8-0.5l8.4,4.9 c0.2,0.1,0.3,0.3,0.3,0.5V27.4z M25.9,22.5c0,0.2-0.1,0.4-0.3,0.5l-8.4,4.9c-0.4,0.2-0.8-0.1-0.8-0.5v-9.7c0-0.2,0.1-0.4,0.3-0.5 l8.4-4.9c0.4-0.2,0.8,0.1,0.8,0.5V22.5z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 1.4 KiB |
43
public/images/logos/resume.svg
Normal file
43
public/images/logos/resume.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 370.123 370.123" style="enable-background:new 0 0 370.123 370.123;" xml:space="preserve">
|
||||
<path d="M321.321,18.74H310.78c0-10.352-8.39-18.74-18.74-18.74H78.083c-10.352,0-18.74,8.389-18.74,18.74H48.802
|
||||
c-10.351,0-18.74,8.389-18.74,18.74c0,10.351,8.39,18.74,18.74,18.74h12.279l22.132,296.557c0.73,9.781,8.879,17.346,18.688,17.346
|
||||
h166.322c9.809,0,17.957-7.564,18.686-17.346l22.133-296.557h12.279c10.351,0,18.74-8.39,18.74-18.74
|
||||
C340.061,27.129,331.672,18.74,321.321,18.74z M119.297,332.643l-7.47-100.061h34.125c8.242,17.324,22.939,28.11,39.109,28.11
|
||||
c16.17,0,30.867-10.786,39.109-28.11h34.125l-7.471,100.061H119.297z M263.007,169.469h-38.836
|
||||
c-8.242-17.324-22.939-28.114-39.109-28.114c-16.17,0-30.867,10.79-39.109,28.114h-38.836L98.665,56.221h172.793L263.007,169.469z"
|
||||
/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -3,8 +3,9 @@
|
||||
FORMAT=$1; shift
|
||||
|
||||
docker run \
|
||||
--workdir "/var/texlive/latex" \
|
||||
--user "$UID:$GID" \
|
||||
--net=none \
|
||||
-v $(pwd):/var/texlive \
|
||||
blang/latex:ubuntu
|
||||
lualatex "latex/$FORMAT.tex"
|
||||
blang/latex:ubuntu \
|
||||
lualatex "$FORMAT.tex"
|
||||
|
||||
Reference in New Issue
Block a user