Add home page with Giraffe/htmx

This commit is contained in:
Daniel J. Summers 2022-12-26 22:29:09 -05:00
parent 5ad408fcfc
commit 9f27d0d7dd
14 changed files with 632 additions and 152 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"editor.inlayHints.enabled": "offUnlessPressed"
}

View File

@ -18,9 +18,9 @@
"date-fns-tz": "^1.1.6",
"dompurify": "^2.3.1",
"marked": "^4.0.18",
"vue": "^3.2.6",
"vue-router": "^4.0.11",
"vuex": "^4.0.0-0"
"vue": "^3.2.45",
"vue-router": "^4.1.6",
"vuex": "^4.1.0"
},
"devDependencies": {
"@types/bootstrap": "^5.1.2",
@ -3090,36 +3090,36 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz",
"integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.37",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz",
"integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"dependencies": {
"@vue/compiler-core": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz",
"integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.37",
"@vue/compiler-dom": "3.2.37",
"@vue/compiler-ssr": "3.2.37",
"@vue/reactivity-transform": "3.2.37",
"@vue/shared": "3.2.37",
"@vue/compiler-core": "3.2.45",
"@vue/compiler-dom": "3.2.45",
"@vue/compiler-ssr": "3.2.45",
"@vue/reactivity-transform": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
@ -3127,12 +3127,12 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz",
"integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"dependencies": {
"@vue/compiler-dom": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-dom": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/@vue/component-compiler-utils": {
@ -3200,9 +3200,9 @@
"dev": true
},
"node_modules/@vue/devtools-api": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.0.tgz",
"integrity": "sha512-pF1G4wky+hkifDiZSWn8xfuLOJI1ZXtuambpBEYaf7Xaf6zC/pM29rvAGpd3qaGXnr4BAXU1Pxz/VfvBGwexGA=="
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
"integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
},
"node_modules/@vue/eslint-config-standard": {
"version": "7.0.0",
@ -3253,60 +3253,60 @@
}
},
"node_modules/@vue/reactivity": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz",
"integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
"dependencies": {
"@vue/shared": "3.2.37"
"@vue/shared": "3.2.45"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz",
"integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.37",
"@vue/shared": "3.2.37",
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz",
"integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
"dependencies": {
"@vue/reactivity": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/reactivity": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz",
"integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
"dependencies": {
"@vue/runtime-core": "3.2.37",
"@vue/shared": "3.2.37",
"@vue/runtime-core": "3.2.45",
"@vue/shared": "3.2.45",
"csstype": "^2.6.8"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz",
"integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
"dependencies": {
"@vue/compiler-ssr": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-ssr": "3.2.45",
"@vue/shared": "3.2.45"
},
"peerDependencies": {
"vue": "3.2.37"
"vue": "3.2.45"
}
},
"node_modules/@vue/shared": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz",
"integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw=="
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"node_modules/@vue/vue-loader-v15": {
"name": "vue-loader",
@ -5190,9 +5190,9 @@
}
},
"node_modules/csstype": {
"version": "2.6.20",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
"version": "2.6.21",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"node_modules/date-fns": {
"version": "2.28.0",
@ -11263,7 +11263,8 @@
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"deprecated": "Please use @jridgewell/sourcemap-codec instead"
},
"node_modules/spdx-correct": {
"version": "3.1.1",
@ -12156,15 +12157,15 @@
}
},
"node_modules/vue": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz",
"integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
"dependencies": {
"@vue/compiler-dom": "3.2.37",
"@vue/compiler-sfc": "3.2.37",
"@vue/runtime-dom": "3.2.37",
"@vue/server-renderer": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-dom": "3.2.45",
"@vue/compiler-sfc": "3.2.45",
"@vue/runtime-dom": "3.2.45",
"@vue/server-renderer": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"node_modules/vue-demi": {
@ -12345,11 +12346,11 @@
}
},
"node_modules/vue-router": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.2.tgz",
"integrity": "sha512-5BP1qXFncVRwgV/XnqzsKApdMjQPqWIpoUBdL1ynz8HyLxIX/UDAx7Ql2BjmA5CXT/p61JfZvkpiFWFpaqcfag==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
"integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==",
"dependencies": {
"@vue/devtools-api": "^6.1.4"
"@vue/devtools-api": "^6.4.5"
},
"funding": {
"url": "https://github.com/sponsors/posva"
@ -12381,14 +12382,14 @@
"dev": true
},
"node_modules/vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
"integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
"dependencies": {
"@vue/devtools-api": "^6.0.0-beta.11"
},
"peerDependencies": {
"vue": "^3.0.2"
"vue": "^3.2.0"
}
},
"node_modules/watchpack": {
@ -15415,36 +15416,36 @@
}
},
"@vue/compiler-core": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.37.tgz",
"integrity": "sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.45.tgz",
"integrity": "sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.37",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"source-map": "^0.6.1"
}
},
"@vue/compiler-dom": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz",
"integrity": "sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz",
"integrity": "sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==",
"requires": {
"@vue/compiler-core": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/compiler-sfc": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz",
"integrity": "sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz",
"integrity": "sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.37",
"@vue/compiler-dom": "3.2.37",
"@vue/compiler-ssr": "3.2.37",
"@vue/reactivity-transform": "3.2.37",
"@vue/shared": "3.2.37",
"@vue/compiler-core": "3.2.45",
"@vue/compiler-dom": "3.2.45",
"@vue/compiler-ssr": "3.2.45",
"@vue/reactivity-transform": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7",
"postcss": "^8.1.10",
@ -15452,12 +15453,12 @@
}
},
"@vue/compiler-ssr": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz",
"integrity": "sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz",
"integrity": "sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==",
"requires": {
"@vue/compiler-dom": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-dom": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/component-compiler-utils": {
@ -15518,9 +15519,9 @@
}
},
"@vue/devtools-api": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.2.0.tgz",
"integrity": "sha512-pF1G4wky+hkifDiZSWn8xfuLOJI1ZXtuambpBEYaf7Xaf6zC/pM29rvAGpd3qaGXnr4BAXU1Pxz/VfvBGwexGA=="
"version": "6.4.5",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
"integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
},
"@vue/eslint-config-standard": {
"version": "7.0.0",
@ -15545,57 +15546,57 @@
}
},
"@vue/reactivity": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.37.tgz",
"integrity": "sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.45.tgz",
"integrity": "sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==",
"requires": {
"@vue/shared": "3.2.37"
"@vue/shared": "3.2.45"
}
},
"@vue/reactivity-transform": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz",
"integrity": "sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz",
"integrity": "sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==",
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.37",
"@vue/shared": "3.2.37",
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
}
},
"@vue/runtime-core": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.37.tgz",
"integrity": "sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.45.tgz",
"integrity": "sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==",
"requires": {
"@vue/reactivity": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/reactivity": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/runtime-dom": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz",
"integrity": "sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz",
"integrity": "sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA==",
"requires": {
"@vue/runtime-core": "3.2.37",
"@vue/shared": "3.2.37",
"@vue/runtime-core": "3.2.45",
"@vue/shared": "3.2.45",
"csstype": "^2.6.8"
}
},
"@vue/server-renderer": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.37.tgz",
"integrity": "sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.45.tgz",
"integrity": "sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g==",
"requires": {
"@vue/compiler-ssr": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-ssr": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"@vue/shared": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.37.tgz",
"integrity": "sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw=="
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.45.tgz",
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg=="
},
"@vue/vue-loader-v15": {
"version": "npm:vue-loader@15.10.0",
@ -16949,9 +16950,9 @@
}
},
"csstype": {
"version": "2.6.20",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
"version": "2.6.21",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
"integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
},
"date-fns": {
"version": "2.28.0",
@ -22029,15 +22030,15 @@
"dev": true
},
"vue": {
"version": "3.2.37",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.37.tgz",
"integrity": "sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==",
"version": "3.2.45",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.45.tgz",
"integrity": "sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA==",
"requires": {
"@vue/compiler-dom": "3.2.37",
"@vue/compiler-sfc": "3.2.37",
"@vue/runtime-dom": "3.2.37",
"@vue/server-renderer": "3.2.37",
"@vue/shared": "3.2.37"
"@vue/compiler-dom": "3.2.45",
"@vue/compiler-sfc": "3.2.45",
"@vue/runtime-dom": "3.2.45",
"@vue/server-renderer": "3.2.45",
"@vue/shared": "3.2.45"
}
},
"vue-demi": {
@ -22157,11 +22158,11 @@
}
},
"vue-router": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.2.tgz",
"integrity": "sha512-5BP1qXFncVRwgV/XnqzsKApdMjQPqWIpoUBdL1ynz8HyLxIX/UDAx7Ql2BjmA5CXT/p61JfZvkpiFWFpaqcfag==",
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.1.6.tgz",
"integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==",
"requires": {
"@vue/devtools-api": "^6.1.4"
"@vue/devtools-api": "^6.4.5"
}
},
"vue-style-loader": {
@ -22189,9 +22190,9 @@
"dev": true
},
"vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
"integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==",
"requires": {
"@vue/devtools-api": "^6.0.0-beta.11"
}

View File

@ -18,9 +18,9 @@
"date-fns-tz": "^1.1.6",
"dompurify": "^2.3.1",
"marked": "^4.0.18",
"vue": "^3.2.6",
"vue-router": "^4.0.11",
"vuex": "^4.0.0-0"
"vue": "^3.2.45",
"vue-router": "^4.1.6",
"vuex": "^4.1.0"
},
"devDependencies": {
"@types/bootstrap": "^5.1.2",

View File

@ -3,7 +3,7 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<DebugType>embedded</DebugType>
<AssemblyVersion>2.2.2.0</AssemblyVersion>
<FileVersion>2.2.2.0</FileVersion>
<AssemblyVersion>3.0.0.0</AssemblyVersion>
<FileVersion>3.0.0.0</FileVersion>
</PropertyGroup>
</Project>

View File

@ -1 +0,0 @@
wwwroot

View File

@ -467,11 +467,21 @@ module Success =
| None -> return! Error.notFound next ctx
}
[<RequireQualifiedAccess>]
module Home =
open JobsJobsJobs.Views
open JobsJobsJobs.Views.Layout
let home : HttpHandler = fun next ctx -> task {
let render = { IsLoggedOn = false; PageTitle = "Welcome"; CurrentUrl = "/"; Content = Home.home }
return! htmlView (view render) next ctx
}
open Giraffe.EndpointRouting
/// All available endpoints for the application
let allEndpoints = [
route "/" Home.home
subRoute "/api" [
subRoute "/citizen" [
GET_HEAD [ routef "/%O" Citizen.get ]

View File

@ -10,6 +10,9 @@
<ItemGroup>
<Compile Include="Auth.fs" />
<Compile Include="Email.fs" />
<Compile Include="Views\Common.fs" />
<Compile Include="Views\Layout.fs" />
<Compile Include="Views\Home.fs" />
<Compile Include="Handlers.fs" />
<Compile Include="App.fs" />
</ItemGroup>
@ -25,6 +28,9 @@
<ItemGroup>
<PackageReference Include="Giraffe" Version="6.0.0" />
<PackageReference Include="Giraffe.Htmx" Version="1.8.4" />
<PackageReference Include="Giraffe.ViewEngine" Version="1.4.0" />
<PackageReference Include="Giraffe.ViewEngine.Htmx" Version="1.8.4" />
<PackageReference Include="MailKit" Version="3.3.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.8" />
<PackageReference Include="Microsoft.FSharpLu.Json" Version="0.11.7" />

View File

@ -0,0 +1,10 @@
[<AutoOpen>]
module JobsJobsJobs.Views.Common
open Giraffe.ViewEngine
/// Create an audio clip with the specified text node
let audioClip clip text =
span [ _class "jjj-audio-clip"; _onclick "jjj.playFile(this)" ] [
text; audio [ _id clip ] [ source [ _src $"/audio/{clip}.mp3" ] ]
]

View File

@ -0,0 +1,72 @@
module JobsJobsJobs.Views.Home
open Giraffe.ViewEngine
/// The home page
let home =
article [] [
p [] [ rawText "&nbsp;" ]
p [] [
rawText "Welcome to Jobs, Jobs, Jobs (AKA No Agenda Careers), where citizens of Gitmo Nation can assist "
rawText "one another in finding employment. This will enable them to continue providing value-for-value to "
rawText "Adam and John, as they continue their work deconstructing the misinformation that passes for news "
rawText "on a day-to-day basis."
]
p [] [
rawText "Do you not understand the terms in the paragraph above? No worries; just head over to "
a [ _href "https://noagendashow.net"; _target "_blank"; _rel "noopener" ] [
rawText "The Best Podcast in the Universe"
]
rawText " "; em [] [ audioClip "thats-true" (rawText "(that&rsquo;s true!)") ]
rawText " and find out what you&rsquo;re missing."
]
]
/// The page for terms of service
let termsOfService =
article [] [
h3 [] [ rawText "Terms of Service" ]
p [ _class "fst-italic" ] [ rawText "(as of August 30<sup>th</sup>, 2022)" ]
h4 [] [ rawText "Acceptance of Terms" ]
p [] [
rawText "By accessing this web site, you are agreeing to be bound by these Terms and Conditions, and that "
rawText "you are responsible to ensure that your use of this site complies with all applicable laws. Your "
rawText "continued use of this site implies your acceptance of these terms."
]
h4 [] [ rawText "Description of Service and Registration" ]
p [] [
rawText "Jobs, Jobs, Jobs is a service that allows individuals to enter and amend employment profiles and "
rawText "job listings, restricting access to the details of these to other users of this site, unless the "
rawText "individual specifies that this information should be visible publicly. See our "
a [ _href "/privacy-policy" ] [ str "privacy policy" ]
rawText " for details on the personal (user) information we maintain."
]
h4 [] [ rawText "Liability" ]
p [] [
rawText "This service is provided &ldquo;as is&rdquo;, and no warranty (express or implied) exists. The "
rawText "service and its developers may not be held liable for any damages that may arise through the use "
rawText "of this service."
]
h4 [] [ rawText "Updates to Terms" ]
p [] [
rawText "These terms and conditions may be updated at any time. When these terms are updated, users will "
rawText "be notified via a notice on the dashboard page. Additionally, the date at the top of this page "
rawText "will be updated, and any substantive updates will also be accompanied by a summary of those "
rawText "changes."
]
hr []
p [] [
rawText "You may also wish to review our "
a [ _href "/privacy-policy" ] [ rawText "privacy policy" ]
rawText " to learn how we handle your data."
]
hr []
p [ _class "fst-italic" ] [
rawText "Change on August 30<sup>th</sup>, 2022 &ndash; added references to job listings, removed "
rawText "references to Mastodon instances."
]
p [ _class "fst-italic" ] [
rawText "Change on September 6<sup>th</sup>, 2021 &ndash; replaced &ldquo;No Agenda Social&rdquo; with a "
rawText "list of all No Agenda-affiliated Mastodon instances."
]
]

View File

@ -0,0 +1,148 @@
module JobsJobsJobs.Views.Layout
open Giraffe.ViewEngine
open Giraffe.ViewEngine.Accessibility
open Giraffe.ViewEngine.Htmx
/// Data items needed to render a view
type PageRenderContext =
{ /// Whether a user is logged on
IsLoggedOn : bool
/// The current URL
CurrentUrl : string
/// The title of this page
PageTitle : string
/// The page content
Content : XmlNode
}
/// Generate the HTML head tag
let private htmlHead ctx =
let pageTitle =
seq {
if ctx.PageTitle <> "" then
ctx.PageTitle; " | "
"Jobs, Jobs, Jobs"
} |> Seq.reduce (+)
head [] [
meta [ _name "viewport"; _content "width=device-width, initial-scale=1" ]
title [] [ str pageTitle ]
link [ _href "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
_rel "stylesheet"
_integrity "sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
_crossorigin "anonymous" ]
link [ _href "https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css"
_rel "stylesheet" ]
link [ _href "/style.css"; _rel "stylesheet" ]
]
/// Display the links available to the current user
let private links ctx =
let navLink url icon text =
a [ _href url
_onclick "jjj.hideMenu()"
if url = ctx.CurrentUrl then _class "jjj-current-page"
] [ i [ _class $"mdi mdi-{icon}"; _ariaHidden "true" ] []; rawText text ]
nav [ _class "jjj-nav" ] [
if ctx.IsLoggedOn then
navLink "/citizen/dashboard" "view-dashboard-variant" "Dashboard"
navLink "/help-wanted" "newspaper-variant-multiple-outline" "Help Wanted!"
navLink "/profile/search" "view-list-outline" "Employment Profiles"
navLink "/success-story/list" "thumb-up" "Success Stories"
div [ _class "separator" ] []
navLink "/citizen/account" "mdiAccountEdit" "My Account"
navLink "/listings/mine" "mdiSignText" "My Job Listings"
navLink "/profile/edit" "mdiPencil" "My Employment Profile"
div [ _class "separator" ] []
navLink "/citizen/log-off" "mdiLogoutVariant" "Log Off"
else
navLink "/" "home" "Home"
navLink "/profile/seeking" "view-list-outline" "Job Seekers"
navLink "/citizen/log-on" "login-variant" "Log On"
navLink "/how-it-works" "help-circle-outline" "How It Works"
]
/// Generate mobile and desktop side navigation areas
let private sideNavs ctx = [
div [ _id "mobileMenu"
_class "jjj-mobile-menu offcanvas offcanvas-end"
_tabindex "-1"
_ariaLabelledBy "mobileMenuLabel" ] [
div [ _class "offcanvas-header" ] [
h5 [ _id "mobileMenuLabel" ] [ rawText "Menu" ]
button [
_class "btn-close text-reset"; _type "button"; _data "bs-dismiss" "offcanvas"; _ariaLabel "Close"
] []
]
div [ _class "offcanvas-body" ] [ links ctx ]
]
aside [ _class "jjj-full-menu d-none d-md-block p-3" ] [
p [ _class "home-link pb-3" ] [ a [ _href "/" ] [ rawText "Jobs, Jobs, Jobs" ] ]
p [] [ rawText "&nbsp;" ]
links ctx
]
]
/// Title bars for mobile and desktop
let private titleBars = [
nav [ _class "d-flex d-md-none navbar navbar-dark" ] [
span [ _class "navbar-text" ] [ a [ _href "/" ] [ rawText "Jobs, Jobs, Jobs" ] ]
button [ _class "btn"
_data "bs-toggle" "offcanvas"
_data "bs-target" "#mobileMenu"
_ariaControls "mobileMenu" ] [ i [ _class "mdi mdi-menu" ] [] ]
]
nav [ _class "d-none d-md-flex navbar navbar-light bg-light"] [
span [] [ rawText "&nbsp;" ]
span [ _class "navbar-text" ] [
rawText "(&hellip;and Jobs &ndash; "
audioClip "pelosi-jobs" (rawText "Let&rsquo;s Vote for Jobs!")
rawText ")"
]
]
]
/// The HTML footer for the page
let private htmlFoot =
let v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version
let version =
seq {
string v.Major
if v.Minor > 0 then
"."; string v.Minor
if v.Build > 0 then
"."; string v.Build
} |> Seq.reduce (+)
footer [] [
p [ _class "text-muted" ] [
str "Jobs, Jobs, Jobs v"; str version; rawText " &bull; "
a [ _href "/privacy-policy" ] [ str "Privacy Policy" ]; rawText " &bull; "
a [ _href "/terms-of-service" ] [ str "Terms of Service" ]
]
]
/// Create a full view
let view ctx =
html [ _lang "en" ] [
htmlHead ctx
body [] [
div [ _class "jjj-app" ] [
yield! sideNavs ctx
//otherSideNav ctx
div [ _class "jjj-main" ] [
yield! titleBars
main [ _class "jjj-content container-fluid" ] [ ctx.Content ]
htmlFoot
]
]
Script.minified
script [ _async
_src "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
_integrity "sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
_crossorigin "anonymous" ] []
script [ _src "/script.js" ] []
]
]

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,19 @@
/** Script for Jobs, Jobs, Jobs */
this.jjj = {
/**
* Play an audio file
* @param {HTMLElement} elt The element which was clicked
*/
playFile(elt) {
elt.querySelector("audio").play()
},
/**
* Hide the offcanvas menu if it displayed
*/
hideMenu() {
/** @type {HTMLElement} */
const menu = document.querySelector(".jjj-mobile-menu")
if (menu.style.display !== "none") bootstrap.Offcanvas.getOrCreateInstance(menu).hide()
}
}

View File

@ -0,0 +1,212 @@
/* Overall styling */
html {
scroll-behavior: smooth;
}
a:link,
a:visited {
text-decoration: none;
}
a:not(.btn):hover {
text-decoration: underline;
}
label.jjj-required::after {
color: red;
content: ' *';
}
.jjj-heading-label {
display: inline-block;
font-size: 1rem;
text-transform: uppercase;
}
.material-icons {
vertical-align: bottom;
}
/* Material Design Icon / Bootstrap styling */
.mdi::before {
font-size: 24px;
line-height: 14px;
}
.btn .mdi::before {
position: relative;
top: 4px;
}
.btn-xs .mdi::before {
font-size: 18px;
top: 3px;
}
.btn-sm .mdi::before {
font-size: 18px;
top: 3px;
}
.dropdown-menu .mdi {
width: 18px;
}
.dropdown-menu .mdi::before {
position: relative;
top: 4px;
left: -8px;
}
.nav .mdi::before {
position: relative;
top: 4px;
}
.navbar .navbar-toggle .mdi::before {
position: relative;
top: 4px;
color: #FFF;
}
.breadcrumb .mdi::before {
position: relative;
top: 4px;
}
.breadcrumb a:hover {
text-decoration: none;
}
.breadcrumb a:hover span {
text-decoration: underline;
}
.alert .mdi::before {
position: relative;
top: 4px;
margin-right: 2px;
}
.input-group-addon .mdi::before {
position: relative;
top: 3px;
}
.navbar-brand .mdi::before {
position: relative;
top: 2px;
margin-right: 2px;
}
.list-group-item .mdi::before {
position: relative;
top: 3px;
left: -3px
}
/* Layout styling */
.jjj-app {
display: flex;
flex-direction: row;
}
.jjj-main {
flex-grow: 1;
display: flex;
flex-flow: column;
min-height: 100vh;
}
.jjj-content {
flex-grow: 2;
}
/* Menu styling */
.jjj-full-menu,
.jjj-mobile-menu {
background-image: linear-gradient(180deg, darkgreen 0%, green 70%);
color: white;
font-size: 1.2rem;
}
.jjj-full-menu {
min-height: 100vh;
width: 250px;
min-width: 250px;
position: sticky;
top: 0;
display: none;
}
.jjj-full-menu .home-link {
font-size: 1.2rem;
text-align: center;
background-color: rgba(0, 0, 0, .4);
margin: -1rem;
padding: 1rem;
}
.jjj-full-menu a:link,
.jjj-full-menu a:visited {
text-decoration: none;
color: white;
}
#jjjMenu {
flex-direction: column;
flex-grow: 1;
}
@media (min-width: 768px) {
.jjj-full-menu {
display: unset;
}
.jjj-mobile-menu {
display: none;
}
.navbar-expand-md .navbar-nav {
flex-direction: column;
}
}
.jjj-nav a:link,
.jjj-nav a:visited {
text-decoration: none;
color: white;
}
nav.jjj-nav > a {
display: block;
width: 100%;
border-radius: .25rem;
padding: .5rem;
margin: .5rem 0;
font-size: 1rem;
}
nav.jjj-nav > a > i {
vertical-align: top;
margin-right: 1rem;
}
nav.jjj-nav > a > i.mdi::before {
line-height: 24px;
}
nav.jjj-nav > a.jjj-current-page {
background-color: rgba(255, 255, 255, .2);
}
nav.jjj-nav > a:hover {
background-color: rgba(255, 255, 255, .5);
color: black;
text-decoration: none;
}
nav.jjj-nav > div.separator {
border-bottom: solid 1px rgba(255, 255, 255, .75);
height: 1px;
}
/* Title bar styling */
.jjj-main .navbar-dark {
background-image: linear-gradient(0deg, green 0%, darkgreen 70%);
padding-left: 1rem;
padding-right: 1rem;
}
.jjj-main .navbar-dark button {
padding: 0;
}
.jjj-main .navbar-dark .navbar-text {
font-weight: bold;
color: white;
}
.jjj-main .navbar-light .navbar-text {
font-style: italic;
padding: 0 1rem 0 0;
}
/* Audio Clip styling */
.jjj-audio-clip audio {
display: none;
}
span.jjj-audio-clip {
border-bottom: dotted 1px lightgray;
}
span.jjj-audio-clip:hover {
cursor: pointer;
}
/* Footer styling */
footer {
display: flex;
flex-direction: row-reverse;
}
footer p {
padding-top: 2rem;
padding-right: .5rem;
font-style: italic;
font-size: .8rem;
}