mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-13 10:11:55 +08:00
Compare commits
369 Commits
feat/resou
...
1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c5d28b68b | ||
|
|
2f8c0c5748 | ||
|
|
5e08af99b2 | ||
|
|
2882815313 | ||
|
|
e093f98a42 | ||
|
|
d3f8e0517b | ||
|
|
efc874163b | ||
|
|
3e8abbed13 | ||
|
|
244d336d8e | ||
|
|
9d24b98f67 | ||
|
|
007bd7feb0 | ||
|
|
7619945028 | ||
|
|
3265f9729d | ||
|
|
4591ad2336 | ||
|
|
163e03f578 | ||
|
|
2215174c20 | ||
|
|
aa52e08ac4 | ||
|
|
96acb6c93e | ||
|
|
69ebd50a90 | ||
|
|
e2eaf7d19c | ||
|
|
da7ba5bf4c | ||
|
|
0d17cf145c | ||
|
|
8a7f7a4a83 | ||
|
|
7d3255576b | ||
|
|
97529cbf54 | ||
|
|
52af178a19 | ||
|
|
f0425fd964 | ||
|
|
570d6a04b1 | ||
|
|
1e677183aa | ||
|
|
14a29b4634 | ||
|
|
bc66a805e3 | ||
|
|
f6c10d7344 | ||
|
|
4e96943374 | ||
|
|
1235459d7a | ||
|
|
0d35fe0364 | ||
|
|
76d6fab581 | ||
|
|
da39147539 | ||
|
|
9e230a01a2 | ||
|
|
4c3452926a | ||
|
|
b36c5b3c26 | ||
|
|
95fe79a0f1 | ||
|
|
850a9ffc9d | ||
|
|
2b8d18d880 | ||
|
|
9b551a69a7 | ||
|
|
cdb9201b2f | ||
|
|
ab15d91614 | ||
|
|
9d2fdfa7cf | ||
|
|
bcad685e27 | ||
|
|
74ae339fe1 | ||
|
|
5811fd8832 | ||
|
|
6c710a92c1 | ||
|
|
51f068085d | ||
|
|
1795bc1495 | ||
|
|
d4ef930187 | ||
|
|
54fae335c2 | ||
|
|
0f886e9def | ||
|
|
3f299cdd8b | ||
|
|
7fccd6b399 | ||
|
|
f75512e96a | ||
|
|
32ff9a700b | ||
|
|
6976202171 | ||
|
|
cf1cca7cb3 | ||
|
|
2236c3f93c | ||
|
|
4f067504a2 | ||
|
|
42747f4f97 | ||
|
|
1d952f75f8 | ||
|
|
9e61ef5dd1 | ||
|
|
c7770cc64c | ||
|
|
af7e25dc92 | ||
|
|
cf3365e778 | ||
|
|
bd69872059 | ||
|
|
746ee3d548 | ||
|
|
73c55a0eaa | ||
|
|
299d0f3ada | ||
|
|
98097f939a | ||
|
|
f4904da3f8 | ||
|
|
465c00b4d5 | ||
|
|
69c54e5dfe | ||
|
|
6f4898c216 | ||
|
|
b8cc07c29e | ||
|
|
eae0ad3ecb | ||
|
|
56bf52e641 | ||
|
|
689f24e0f1 | ||
|
|
63d66b3f4e | ||
|
|
4930c00f78 | ||
|
|
5745fc56bf | ||
|
|
55d5ced587 | ||
|
|
018be76895 | ||
|
|
b268106684 | ||
|
|
56e2108be2 | ||
|
|
9dfbceda7c | ||
|
|
c698265f42 | ||
|
|
752d4614b8 | ||
|
|
d73e08f8f6 | ||
|
|
cf648924cf | ||
|
|
2d15290566 | ||
|
|
06dd1934f3 | ||
|
|
316ada1259 | ||
|
|
30d2f15433 | ||
|
|
4ac1319d8d | ||
|
|
4e924981c1 | ||
|
|
fdf3fd050b | ||
|
|
79afd0a6a8 | ||
|
|
03e35ee928 | ||
|
|
eaaedb8034 | ||
|
|
84e87a501e | ||
|
|
8fca669787 | ||
|
|
3c1d41119f | ||
|
|
495fd37eae | ||
|
|
4cfeb1c372 | ||
|
|
91a47faec0 | ||
|
|
8c03aedea1 | ||
|
|
9a515f85c1 | ||
|
|
0a2468aad2 | ||
|
|
fc2eb36d58 | ||
|
|
3c5ea2131d | ||
|
|
75e1f67ee8 | ||
|
|
b40894cfdc | ||
|
|
4fb2e1f46d | ||
|
|
8eccfd22e3 | ||
|
|
d84800fcaf | ||
|
|
bb3260f4b7 | ||
|
|
9a2e1fd673 | ||
|
|
3f599fab35 | ||
|
|
cdc710123f | ||
|
|
bb43c8eba6 | ||
|
|
c01d595546 | ||
|
|
77a66fd25d | ||
|
|
a93ac86766 | ||
|
|
4044dbea91 | ||
|
|
3fc9ffe8b4 | ||
|
|
880475f6de | ||
|
|
a26945288b | ||
|
|
b97ae52a1b | ||
|
|
76ddeeedb2 | ||
|
|
00b7fe6e7f | ||
|
|
c43442f127 | ||
|
|
68c62d218d | ||
|
|
47b10a1a1a | ||
|
|
1fd135d1c1 | ||
|
|
61bdc80f5a | ||
|
|
4fbefd5ae9 | ||
|
|
835476ed31 | ||
|
|
83745ae1b4 | ||
|
|
9465cfb5c2 | ||
|
|
4edd398770 | ||
|
|
21b3b7cbdf | ||
|
|
ae6763bf83 | ||
|
|
be5a61b697 | ||
|
|
8e25dca636 | ||
|
|
b91d404f17 | ||
|
|
80f2cb8cbc | ||
|
|
2dc3d4fd24 | ||
|
|
2432ff9fd4 | ||
|
|
8f1f8846c9 | ||
|
|
7dac8665a0 | ||
|
|
f0181ff08f | ||
|
|
0ad95c2dd0 | ||
|
|
d184e93519 | ||
|
|
4ef31700a5 | ||
|
|
087f4e5c25 | ||
|
|
c5ae26458a | ||
|
|
0c6de5d89b | ||
|
|
124d113162 | ||
|
|
c88b0f3b1a | ||
|
|
06d72599d9 | ||
|
|
eb9cd6cdcc | ||
|
|
c7589b8325 | ||
|
|
4c07ac509b | ||
|
|
1240b6b1bc | ||
|
|
ad05c49570 | ||
|
|
c01a854a5a | ||
|
|
7b1dde1d62 | ||
|
|
56b0275b06 | ||
|
|
7a0d784d81 | ||
|
|
2c9eb1f9ee | ||
|
|
e4ca1c9598 | ||
|
|
2b8e06d651 | ||
|
|
56088a838c | ||
|
|
542d82c2dc | ||
|
|
980322bae0 | ||
|
|
56fbe9a685 | ||
|
|
6939240d59 | ||
|
|
4caaee3da5 | ||
|
|
e829af3e62 | ||
|
|
7ba0fa9004 | ||
|
|
74433cd0d3 | ||
|
|
dec3e992b3 | ||
|
|
7a4c27460f | ||
|
|
5553b411eb | ||
|
|
98cc968ed1 | ||
|
|
3de37468a6 | ||
|
|
3364eae0a6 | ||
|
|
a06eaec5d4 | ||
|
|
10e433f538 | ||
|
|
129deed6a9 | ||
|
|
ce35a8112f | ||
|
|
35f6070133 | ||
|
|
629f1058f2 | ||
|
|
199310df93 | ||
|
|
0d45fcbf79 | ||
|
|
47cbcde5dc | ||
|
|
5b12eb9e02 | ||
|
|
6632b46d98 | ||
|
|
25e009a63f | ||
|
|
9ae7eed1e3 | ||
|
|
8db62cb19f | ||
|
|
d1a991b18c | ||
|
|
8107e008ff | ||
|
|
944858bbb1 | ||
|
|
b864c60ea3 | ||
|
|
618b55f601 | ||
|
|
b5c65b408b | ||
|
|
21f2ef80ba | ||
|
|
ebd351e133 | ||
|
|
77dab81b92 | ||
|
|
0350da2929 | ||
|
|
59c07c9000 | ||
|
|
79ab31dec7 | ||
|
|
16983cb950 | ||
|
|
e29fe52cb1 | ||
|
|
7921acb666 | ||
|
|
b53f8c982c | ||
|
|
0b72a07147 | ||
|
|
5155a0c358 | ||
|
|
bd5663ab26 | ||
|
|
af3ccd5bb5 | ||
|
|
035eaa47e8 | ||
|
|
3541d4e717 | ||
|
|
e8dcfe97f2 | ||
|
|
8f3307e53e | ||
|
|
dcc825416d | ||
|
|
f8fcb8d600 | ||
|
|
40919dec14 | ||
|
|
f3592155bf | ||
|
|
927ee73be7 | ||
|
|
4f81d5374e | ||
|
|
e95fd69886 | ||
|
|
11d9da5afb | ||
|
|
cea8abc5ef | ||
|
|
7169d3bb8f | ||
|
|
ae9c1c4992 | ||
|
|
58e560af7d | ||
|
|
09fa166f56 | ||
|
|
6ed7d9c25f | ||
|
|
e59fc5e4e9 | ||
|
|
f5da05c3ec | ||
|
|
07b200b878 | ||
|
|
ccca782f25 | ||
|
|
77d9846d9b | ||
|
|
8da175e9d8 | ||
|
|
467634889b | ||
|
|
b46b425b41 | ||
|
|
9e23439f0c | ||
|
|
c6db625e35 | ||
|
|
672245e4e4 | ||
|
|
e4ce3475c6 | ||
|
|
d15b97db73 | ||
|
|
8f040e5e8a | ||
|
|
888800d2a0 | ||
|
|
51b2c70586 | ||
|
|
5b4cc86f61 | ||
|
|
9952ee5805 | ||
|
|
dacbf09f55 | ||
|
|
a16787ab58 | ||
|
|
7d45c8e462 | ||
|
|
796bde76c9 | ||
|
|
22d5622e1e | ||
|
|
2312fdd608 | ||
|
|
bc2ecea03b | ||
|
|
84a551f906 | ||
|
|
9fab5c7134 | ||
|
|
c61f4a845d | ||
|
|
025753b279 | ||
|
|
6b9901db28 | ||
|
|
34f0e483ec | ||
|
|
0ae9bc0e3e | ||
|
|
3f17f60daf | ||
|
|
7f2acba352 | ||
|
|
907fb9915f | ||
|
|
fd2e64ec50 | ||
|
|
3fd5b9e744 | ||
|
|
edff9156ff | ||
|
|
e1c89585e9 | ||
|
|
abaa839b26 | ||
|
|
1bc7384929 | ||
|
|
6a148295f7 | ||
|
|
ea25f2d99b | ||
|
|
08303c0623 | ||
|
|
f18f9fb5b3 | ||
|
|
dfc07e0753 | ||
|
|
64a19fdc3c | ||
|
|
1b3e8712ff | ||
|
|
f242c6e358 | ||
|
|
7e2121bed9 | ||
|
|
bb80ceb7ba | ||
|
|
25dfb28368 | ||
|
|
a1c75bb9f8 | ||
|
|
efdb628120 | ||
|
|
ac23dddeb9 | ||
|
|
b208eaa1bd | ||
|
|
8ebf97277c | ||
|
|
928d79e3fb | ||
|
|
9b95218eb8 | ||
|
|
8bcdd84f0f | ||
|
|
67a72aab11 | ||
|
|
771f3a9cb7 | ||
|
|
38b6b34437 | ||
|
|
548dfd85e7 | ||
|
|
971d23c43a | ||
|
|
3aac8de849 | ||
|
|
8d605735b2 | ||
|
|
7debdb90c1 | ||
|
|
f6f5c821b3 | ||
|
|
227e08b7c4 | ||
|
|
16651606fb | ||
|
|
0ea67f695d | ||
|
|
6c4386ed7d | ||
|
|
e65ba9365b | ||
|
|
e5843568dd | ||
|
|
7968151c44 | ||
|
|
9f0753f098 | ||
|
|
98d0aa5103 | ||
|
|
c1706e2c18 | ||
|
|
84e74096b7 | ||
|
|
3d96fdf1df | ||
|
|
a157605b2b | ||
|
|
ec83830577 | ||
|
|
6babeb3f21 | ||
|
|
0b9754c9ae | ||
|
|
c2f7754b0d | ||
|
|
4df519845f | ||
|
|
910bd371dd | ||
|
|
0aa6db6007 | ||
|
|
01be603780 | ||
|
|
55a3ce4def | ||
|
|
a21264eb5e | ||
|
|
8c216782e5 | ||
|
|
ed9823245b | ||
|
|
378e53eba4 | ||
|
|
66b68bc26f | ||
|
|
0785d28bb4 | ||
|
|
f43dda522d | ||
|
|
ba98142d5b | ||
|
|
43160d3058 | ||
|
|
d40a858c6a | ||
|
|
4024005c4a | ||
|
|
91d1fc7245 | ||
|
|
328efa6ff6 | ||
|
|
cb352aba68 | ||
|
|
625ca5dcf4 | ||
|
|
25d686ae5c | ||
|
|
0ab94faa95 | ||
|
|
5299a04acd | ||
|
|
f326a58bee | ||
|
|
d8d52a6e86 | ||
|
|
ba09cc4b86 | ||
|
|
5804deb8ac | ||
|
|
63b3f0199b | ||
|
|
0b0addaee4 | ||
|
|
aab6d380aa | ||
|
|
79b5c09a06 | ||
|
|
3bd4ad5874 | ||
|
|
79887dc7d5 | ||
|
|
dc8cb8e777 | ||
|
|
a8059e73c0 | ||
|
|
ee2b3e5de0 | ||
|
|
807e5ea2c1 | ||
|
|
f7b42203a4 |
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"devToolbar": {
|
||||
"enabled": false
|
||||
},
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1723855511353
|
||||
}
|
||||
}
|
||||
13
.babelrc
Normal file
13
.babelrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"presets": [
|
||||
"next/babel"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"styled-components",
|
||||
{
|
||||
"ssr": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
23
.editorconfig
Normal file
23
.editorconfig
Normal file
@@ -0,0 +1,23 @@
|
||||
# EditorConfig for the node-soap library - head over to editorconfig.org to see if you editor supports this file.
|
||||
|
||||
# this is the topmost .editorconfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.xml]
|
||||
insert_final_newline = false
|
||||
|
||||
[*.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{package.json, .travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1,3 +0,0 @@
|
||||
PUBLIC_API_URL=https://api.roadmap.sh
|
||||
PUBLIC_AVATAR_BASE_URL=https://dodrc8eu8m09s.cloudfront.net/avatars
|
||||
PUBLIC_EDITOR_APP_URL=https://draw.roadmap.sh
|
||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,2 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: kamranahmedse
|
||||
|
||||
36
.github/ISSUE_TEMPLATE.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
Please do not remove anything written below.
|
||||
|
||||
Fill the details and open the issue. Any issue that
|
||||
doesn't have all of these filled in will be closed,
|
||||
if yours is closed reopen with everything filled in.
|
||||
-->
|
||||
|
||||
#### What roadmap is this issue about?
|
||||
|
||||
- [ ] Frontend Roadmap
|
||||
- [ ] Backend Roadmap
|
||||
- [ ] DevOps Roadmap
|
||||
- [ ] All Roadmaps
|
||||
|
||||
#### What is this issue about?
|
||||
|
||||
- [ ] Functionality of the website
|
||||
- [ ] Discussion for a pull request I would want to open.
|
||||
- [ ] Addition of a new item
|
||||
- [ ] Removal of some existing item
|
||||
- [ ] Changing in arrangement
|
||||
- [ ] General suggestion
|
||||
- [ ] Sharing an Idea
|
||||
- [ ] Something else
|
||||
|
||||
#### Please acknowledge the below listed
|
||||
|
||||
- [ ] This is not a duplicate issue. I have searched and there is no existing issue for this.
|
||||
- [ ] I understand that these roadmaps are highly opinionated. The purpose is to not to include everything out there in these roadmaps but to have everything that is most relevant today comparing to the other options listed.
|
||||
- [ ] I have read the [contribution docs](../contributing) before opening this issue.
|
||||
|
||||
|
||||
#### Enter the details about the issue here
|
||||
|
||||
<!-- Please enter the issue details here -->
|
||||
25
.github/ISSUE_TEMPLATE/01-suggest-changes.yml
vendored
25
.github/ISSUE_TEMPLATE/01-suggest-changes.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: "✍️ Missing or Deprecated Roadmap Topics"
|
||||
description: Help us improve the roadmaps by suggesting changes
|
||||
labels: [topic-change]
|
||||
assignees: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to help us improve the roadmaps with your suggestions.
|
||||
- type: input
|
||||
id: url
|
||||
attributes:
|
||||
label: Roadmap URL
|
||||
description: Please provide the URL of the roadmap you are suggesting changes to.
|
||||
placeholder: https://roadmap.sh
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: roadmap-suggestions
|
||||
attributes:
|
||||
label: Suggestions
|
||||
description: What changes would you like to suggest?
|
||||
placeholder: Enter your suggestions here.
|
||||
validations:
|
||||
required: true
|
||||
42
.github/ISSUE_TEMPLATE/02-bug-report.yml
vendored
42
.github/ISSUE_TEMPLATE/02-bug-report.yml
vendored
@@ -1,42 +0,0 @@
|
||||
name: "🐛 Bug Report"
|
||||
description: Report an issue or possible bug
|
||||
labels: [bug]
|
||||
assignees: []
|
||||
body:
|
||||
- type: input
|
||||
id: url
|
||||
attributes:
|
||||
label: What is the URL where the issue is happening
|
||||
placeholder: https://roadmap.sh
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: browsers
|
||||
attributes:
|
||||
label: What browsers are you seeing the problem on?
|
||||
multiple: true
|
||||
options:
|
||||
- Firefox
|
||||
- Chrome
|
||||
- Safari
|
||||
- Microsoft Edge
|
||||
- Other
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Output from browser console (if any)
|
||||
description: Please copy and paste any relevant log output.
|
||||
- type: checkboxes
|
||||
id: will-pr
|
||||
attributes:
|
||||
label: Participation
|
||||
options:
|
||||
- label: I am willing to submit a pull request for this issue.
|
||||
required: false
|
||||
12
.github/ISSUE_TEMPLATE/03-feature-suggestion.yml
vendored
12
.github/ISSUE_TEMPLATE/03-feature-suggestion.yml
vendored
@@ -1,12 +0,0 @@
|
||||
name: "✨ Feature Suggestion"
|
||||
description: Is there a feature you'd like to see on Roadmap.sh? Let us know!
|
||||
labels: [feature request]
|
||||
assignees: []
|
||||
body:
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: Feature Description
|
||||
description: Please provide a detailed description of the feature you are suggesting and how it would help you/others.
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,25 +0,0 @@
|
||||
name: "🙏 Submit a Roadmap"
|
||||
description: Help us launch a new roadmap with your expertise.
|
||||
labels: [roadmap contribution]
|
||||
assignees: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to submit a roadmap! Please fill out the information below and we'll get back to you as soon as we can.
|
||||
- type: input
|
||||
id: roadmap-title
|
||||
attributes:
|
||||
label: What is the title of the roadmap you are submitting?
|
||||
placeholder: e.g. Roadmap to learn Data Science
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: roadmap-description
|
||||
attributes:
|
||||
label: Roadmap Link
|
||||
description: Please create the roadmap [using our roadmap editor](https://twitter.com/kamrify/status/1708293162693767426) and submit the roadmap link.
|
||||
placeholder: |
|
||||
https://roadmap.sh/xyz
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,35 +0,0 @@
|
||||
name: "🙏 Submit a Project Idea"
|
||||
description: Help us add project ideas to roadmaps.
|
||||
labels: [project contribution]
|
||||
assignees: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to submit a project idea! Please fill out the information below and we'll get back to you as soon as we can.
|
||||
- type: input
|
||||
id: roadmap-title
|
||||
attributes:
|
||||
label: What Roadmap is this project for?
|
||||
placeholder: e.g. Backend Roadmap
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: project-difficulty
|
||||
attributes:
|
||||
label: Project Difficulty
|
||||
options:
|
||||
- Beginner
|
||||
- Intermediate
|
||||
- Advanced
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: roadmap-description
|
||||
attributes:
|
||||
label: Add Project Details
|
||||
description: Please write a detailed description of the project in 3rd person e.g. "You are required to build a..."
|
||||
placeholder: |
|
||||
e.g. You are required to build a RESTful API...
|
||||
validations:
|
||||
required: true
|
||||
12
.github/ISSUE_TEMPLATE/05-something-else.yml
vendored
12
.github/ISSUE_TEMPLATE/05-something-else.yml
vendored
@@ -1,12 +0,0 @@
|
||||
name: "🤷♂️ Something else"
|
||||
description: If none of the above templates fit your needs, please use this template to submit your issue.
|
||||
labels: []
|
||||
assignees: []
|
||||
body:
|
||||
- type: textarea
|
||||
id: issue-description
|
||||
attributes:
|
||||
label: Detailed Description
|
||||
description: Please provide a detailed description of the issue.
|
||||
validations:
|
||||
required: true
|
||||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
14
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,14 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: ✋ Roadmap Request
|
||||
url: https://roadmap.sh/discord
|
||||
about: Please do not open issues with roadmap requests, hop onto the discord server for that.
|
||||
- name: 📝 Typo or Grammatical Mistake
|
||||
url: https://github.com/kamranahmedse/developer-roadmap/tree/master/src/data
|
||||
about: Please submit a pull request instead of reporting it as an issue.
|
||||
- name: 💬 Chat on Discord
|
||||
url: https://roadmap.sh/discord
|
||||
about: Join the community on our Discord server.
|
||||
- name: 🤝 Guidance
|
||||
url: https://roadmap.sh/discord
|
||||
about: Join the community in our Discord server.
|
||||
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
19
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#### What roadmap does this PR target?
|
||||
|
||||
- [ ] Code Change
|
||||
- [ ] Frontend Roadmap
|
||||
- [ ] Backend Roadmap
|
||||
- [ ] DevOps Roadmap
|
||||
- [ ] All Roadmaps
|
||||
- [ ] Guides
|
||||
|
||||
#### Please acknowledge the items listed below
|
||||
|
||||
- [ ] I have discussed this contribution and got a go-ahead in an issue before opening this pull request.
|
||||
- [ ] This is not a duplicate issue. I have searched and there is no existing issue for this.
|
||||
- [ ] I understand that these roadmaps are highly opinionated. The purpose is to not to include everything out there in these roadmaps but to have everything that is most relevant today comparing to the other options listed.
|
||||
- [ ] I have read the [contribution docs](../contributing) before opening this PR.
|
||||
|
||||
#### Enter the details about the contribution
|
||||
|
||||
<!-- Enter the details here -->
|
||||
21
.github/workflows/aws-costs.yml
vendored
21
.github/workflows/aws-costs.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Sends Daily AWS Costs to Slack
|
||||
on:
|
||||
# Allow manual Run
|
||||
workflow_dispatch:
|
||||
# Run at 7:00 UTC every day
|
||||
schedule:
|
||||
- cron: "0 7 * * *"
|
||||
jobs:
|
||||
aws_costs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Costs
|
||||
env:
|
||||
AWS_KEY: ${{ secrets.COST_AWS_ACCESS_KEY }}
|
||||
AWS_SECRET: ${{ secrets.COST_AWS_SECRET_KEY }}
|
||||
AWS_REGION: ${{ secrets.COST_AWS_REGION }}
|
||||
SLACK_CHANNEL: ${{ secrets.SLACK_COST_CHANNEL }}
|
||||
SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}
|
||||
run: |
|
||||
npm install -g aws-cost-cli
|
||||
aws-cost -k $AWS_KEY -s $AWS_SECRET -r $AWS_REGION -S $SLACK_TOKEN -C $SLACK_CHANNEL
|
||||
50
.github/workflows/close-feedback-pr.yml
vendored
50
.github/workflows/close-feedback-pr.yml
vendored
@@ -1,50 +0,0 @@
|
||||
name: Close PRs with Feedback
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
jobs:
|
||||
close-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close PR if it has label "feedback left" and no changes in 7 days
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const { data: pullRequests } = await github.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
base: 'master',
|
||||
});
|
||||
|
||||
for (const pullRequest of pullRequests) {
|
||||
const { data: labels } = await github.issues.listLabelsOnIssue({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
});
|
||||
|
||||
const feedbackLabel = labels.find((label) => label.name === 'feedback left');
|
||||
if (feedbackLabel) {
|
||||
const lastUpdated = new Date(pullRequest.updated_at);
|
||||
const sevenDaysAgo = new Date();
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
||||
|
||||
if (lastUpdated < sevenDaysAgo) {
|
||||
await github.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pullRequest.number,
|
||||
body: 'Closing this PR because there has been no activity for the past 7 days. Feel free to reopen if you have any feedback.',
|
||||
});
|
||||
await github.pulls.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pullRequest.number,
|
||||
state: 'closed',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
16
.github/workflows/cloudfront-cache.yml
vendored
16
.github/workflows/cloudfront-cache.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Clears Cloudfront Cache
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
aws_costs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clear Cloudfront Caching
|
||||
run: |
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GH_PAT }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/roadmapsh/infra-ansible/actions/workflows/playbook.yml/dispatches \
|
||||
-d '{ "ref":"master", "inputs": { "playbook": "roadmap_web.yml", "tags": "cloudfront", "is_verbose": false } }'
|
||||
31
.github/workflows/deploy.yml
vendored
Normal file
31
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
name: Deployment to GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
env:
|
||||
ROADMAP_GA_SECRET: ${{ secrets.GA_SECRET }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CI: true
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 12
|
||||
- name: Setup Environment
|
||||
run: |
|
||||
npm install -g yarn
|
||||
yarn install
|
||||
- name: Generate meta and builld
|
||||
run: |
|
||||
yarn meta
|
||||
yarn build
|
||||
- name: Deploy to GitHub Pages
|
||||
run: |
|
||||
git config user.email "kamranahmed.se@gmail.com"
|
||||
git config user.name "Kamran Ahmed"
|
||||
git remote set-url origin https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
|
||||
npm run deploy
|
||||
72
.github/workflows/deployment.yml
vendored
72
.github/workflows/deployment.yml
vendored
@@ -1,72 +0,0 @@
|
||||
name: Deploy to EC2
|
||||
on:
|
||||
workflow_dispatch: # allow manual run
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v3.0.0
|
||||
with:
|
||||
version: 8.15.6
|
||||
|
||||
# --------------------
|
||||
# Setup configuration
|
||||
# --------------------
|
||||
- name: Prepare configuration files
|
||||
run: |
|
||||
git clone https://${{ secrets.GH_PAT }}@github.com/roadmapsh/infra-config.git configuration --depth 1
|
||||
- name: Copy configuration files
|
||||
run: |
|
||||
cp configuration/dist/github/developer-roadmap.env .env
|
||||
|
||||
# --------------------
|
||||
# Prepare the build
|
||||
# --------------------
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
- name: Generate build
|
||||
run: |
|
||||
git clone https://${{ secrets.GH_PAT }}@github.com/roadmapsh/web-draw.git .temp/web-draw --depth 1
|
||||
npm run generate-renderer
|
||||
npm run compress:images
|
||||
npm run build
|
||||
|
||||
# --------------------
|
||||
# Deploy to EC2
|
||||
# --------------------
|
||||
- uses: webfactory/ssh-agent@v0.7.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.EC2_PRIVATE_KEY }}
|
||||
- name: Deploy app to EC2
|
||||
run: |
|
||||
rsync -apvz --delete --no-times --exclude "configuration" -e "ssh -o StrictHostKeyChecking=no" -p ./ ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/var/www/roadmap.sh/
|
||||
- name: Restart PM2
|
||||
uses: appleboy/ssh-action@master
|
||||
with:
|
||||
host: ${{ secrets.EC2_HOST }}
|
||||
username: ${{ secrets.EC2_USERNAME }}
|
||||
key: ${{ secrets.EC2_PRIVATE_KEY }}
|
||||
script: |
|
||||
cd /var/www/roadmap.sh
|
||||
sudo pm2 restart web-roadmap
|
||||
|
||||
# --------------------
|
||||
# Clear cloudfront cache
|
||||
# --------------------
|
||||
- name: Clear Cloudfront Caching
|
||||
run: |
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.GH_PAT }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/roadmapsh/infra-ansible/actions/workflows/playbook.yml/dispatches \
|
||||
-d '{ "ref":"master", "inputs": { "playbook": "roadmap_web.yml", "tags": "cloudfront", "is_verbose": false } }'
|
||||
38
.github/workflows/label-issue.yml
vendored
38
.github/workflows/label-issue.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Label Issue
|
||||
on:
|
||||
issues:
|
||||
types: [ opened, edited ]
|
||||
jobs:
|
||||
label-topic-change-issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add roadmap slug to issue as label
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const roadmapUrl = issue.body.match(/https?:\/\/roadmap.sh\/[^ ]+/);
|
||||
|
||||
// if the issue is labeled as a topic-change, add the roadmap slug as a label
|
||||
if (issue.labels.some(label => label.name === 'topic-change')) {
|
||||
if (roadmapUrl) {
|
||||
const roadmapSlug = new URL(roadmapUrl[0]).pathname.replace(/\//, '');
|
||||
github.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [roadmapSlug]
|
||||
});
|
||||
}
|
||||
|
||||
// Close the issue if it has no roadmap URL
|
||||
if (!roadmapUrl) {
|
||||
github.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
name: Refreshes roadmap content JSON
|
||||
|
||||
on:
|
||||
workflow_dispatch: # allow manual run
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # every day at midnight
|
||||
|
||||
jobs:
|
||||
upgrade-deps:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies and generate content JSON
|
||||
run: |
|
||||
pnpm install
|
||||
npm run generate:roadmap-content-json
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
delete-branch: false
|
||||
branch: "chore/update-content-json"
|
||||
base: "master"
|
||||
labels: |
|
||||
dependencies
|
||||
automated pr
|
||||
reviewers: kamranahmedse,arikchakma
|
||||
commit-message: "chore: update roadmap content json"
|
||||
title: "Update roadmap content json"
|
||||
body: |
|
||||
Updates the roadmap content JSON files in the `public` folder.
|
||||
Please review the changes and merge if everything looks good.
|
||||
38
.github/workflows/update-deps.yml
vendored
38
.github/workflows/update-deps.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Update dependencies
|
||||
|
||||
on:
|
||||
workflow_dispatch: # allow manual run
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # every sunday at midnight
|
||||
|
||||
jobs:
|
||||
upgrade-deps:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- uses: pnpm/action-setup@v2.2.2
|
||||
with:
|
||||
version: 7.13.4
|
||||
- name: Upgrade dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
npm run upgrade
|
||||
pnpm install --lockfile-only
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
delete-branch: false
|
||||
branch: "update-deps"
|
||||
base: "master"
|
||||
labels: |
|
||||
dependencies
|
||||
automated pr
|
||||
reviewers: kamranahmedse
|
||||
commit-message: "chore: update dependencies to latest"
|
||||
title: "Upgrade dependencies to latest"
|
||||
body: |
|
||||
Updates all dependencies to latest versions.
|
||||
Please review the changes and merge if everything looks good.
|
||||
47
.gitignore
vendored
47
.gitignore
vendored
@@ -1,35 +1,14 @@
|
||||
.idea
|
||||
.temp
|
||||
|
||||
# build output
|
||||
dist/
|
||||
.output/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
scripts/developer-roadmap
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
_*
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
tests-examples
|
||||
*.csv
|
||||
|
||||
/editor/*
|
||||
!/editor/readonly-editor.tsx
|
||||
!/editor/renderer/renderer.ts
|
||||
!/editor/renderer/index.tsx
|
||||
.idea
|
||||
.next
|
||||
out
|
||||
.env
|
||||
build
|
||||
node_modules
|
||||
yarn-error.log
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
config/*.json
|
||||
!config/dev.json
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
app-dist
|
||||
dist
|
||||
.idea
|
||||
.github
|
||||
public
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
@@ -1,18 +0,0 @@
|
||||
module.exports = {
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
overrides: [
|
||||
{
|
||||
files: '*.astro',
|
||||
options: {
|
||||
parser: 'astro',
|
||||
singleQuote: true,
|
||||
jsxSingleQuote: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
plugins: [
|
||||
require.resolve('prettier-plugin-astro'),
|
||||
'prettier-plugin-tailwindcss',
|
||||
],
|
||||
};
|
||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
11
.vscode/launch.json
vendored
11
.vscode/launch.json
vendored
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"prettier.documentSelectors": ["**/*.astro"],
|
||||
"[astro]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
76
CODE_OF_CONDUCT.md
Normal file
76
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at <kamranahmed.se@gmail.com>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
20
LICENSE
Normal file
20
LICENSE
Normal file
@@ -0,0 +1,20 @@
|
||||
The codebase in this repository is covered under BSD 4-Clause license and
|
||||
the content with CC BY-NC-ND 3.0 with additional clauses
|
||||
|
||||
Everything including text and images in this project are protected by the copyright laws.
|
||||
You are allowed to use this material for personal use but are not allowed to use it for
|
||||
any other purpose including publishing the images, the project files or the content in the
|
||||
images in any form either digital, non-digital, textual, graphical or written formats.
|
||||
You are allowed to share the links to the repository or the website roadmap.sh but not
|
||||
the content for any sort of usage that involves the content of this repository taken out
|
||||
of the repository and be shared from any other medium including but not limited to blog
|
||||
posts, articles, newsletters, you must get prior consent from the understated.
|
||||
|
||||
Copyright © 2021 Kamran Ahmed <kamranahmed.se@gmail.com>
|
||||
|
||||
Please note that I am really flexible with allowing the usage of the content in this
|
||||
repository. If you reach out to me with a brief detail of why and how you would like
|
||||
to use this content, there is a good chance that I will allow you to use it. The reason
|
||||
behind this strictness in the license is to stop the people who have been using these
|
||||
roadmaps in ill manners e.g. ripping people off with suggesting random affiliate links,
|
||||
redistributing these roadmaps just for the sake of monetizing the traffic.
|
||||
@@ -1,60 +0,0 @@
|
||||
// https://astro.build/config
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import node from '@astrojs/node';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import rehypeExternalLinks from 'rehype-external-links';
|
||||
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
|
||||
|
||||
import react from '@astrojs/react';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://roadmap.sh/',
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
theme: 'dracula',
|
||||
},
|
||||
rehypePlugins: [
|
||||
[
|
||||
rehypeExternalLinks,
|
||||
{
|
||||
target: '_blank',
|
||||
rel: function (element) {
|
||||
const href = element.properties.href;
|
||||
const whiteListedStarts = [
|
||||
'/',
|
||||
'#',
|
||||
'mailto:',
|
||||
'https://github.com/kamranahmedse',
|
||||
'https://thenewstack.io',
|
||||
'https://kamranahmed.info',
|
||||
'https://roadmap.sh',
|
||||
];
|
||||
if (whiteListedStarts.some((start) => href.startsWith(start))) {
|
||||
return [];
|
||||
}
|
||||
return 'noopener noreferrer nofollow';
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
output: 'hybrid',
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
}),
|
||||
trailingSlash: 'never',
|
||||
integrations: [
|
||||
tailwind({
|
||||
config: {
|
||||
applyBaseStyles: false,
|
||||
},
|
||||
}),
|
||||
sitemap({
|
||||
filter: shouldIndexPage,
|
||||
serialize: serializeSitemap,
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
});
|
||||
@@ -1,76 +0,0 @@
|
||||
# Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to make participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all project spaces, and it also applies when
|
||||
an individual is representing the project or its community in public spaces.
|
||||
Examples of representing a project or community include using an official
|
||||
project e-mail address, posting via an official social media account, or acting
|
||||
as an appointed representative at an online or offline event. Representation of
|
||||
a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at <kamranahmed.se@gmail.com>. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
22
components/about-header/index.js
Normal file
22
components/about-header/index.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { AboutHeaderWrap } from './style';
|
||||
import siteConfig from "content/site";
|
||||
|
||||
const AboutHeader = () => (
|
||||
<AboutHeaderWrap>
|
||||
<div className="container container-small">
|
||||
<div className="author-info">
|
||||
<img className='author-img d-none d-sm-none d-md-block d-lg-block d-xl-block' src="/kamran.jpeg" />
|
||||
<div className="author-msg">
|
||||
<h1>Hello, I'm Kamran Ahmed.</h1>
|
||||
<p>I created <span className='flow-black'>roadmap.sh</span> to help developers find their path if they are confused and help them grow in their career.</p>
|
||||
<div className="author-links">
|
||||
<a href={`https://twitter.com/${siteConfig.twitter}`} target="_blank">@kamranahmedse</a>
|
||||
<a href="mailto:kamran@roadmap.sh">kamran@roadmap.sh</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AboutHeaderWrap>
|
||||
);
|
||||
|
||||
export default AboutHeader;
|
||||
49
components/about-header/style.js
Normal file
49
components/about-header/style.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const AboutHeaderWrap = styled.div`
|
||||
text-align: left;
|
||||
padding: 70px 0;
|
||||
margin: 0 auto;
|
||||
|
||||
.author-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
font-weight: 400;
|
||||
margin-bottom: 0;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.author-img {
|
||||
height: 160px;
|
||||
margin-right: 25px;
|
||||
margin-top: 8px;
|
||||
border-radius: 9px;
|
||||
}
|
||||
|
||||
.author-links a {
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
background: #2d2d2d;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
font-weight: 500;
|
||||
margin-top: 12px;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
54
components/badges/index.js
Normal file
54
components/badges/index.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const BadgesList = styled.p`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
export const PrimaryBadge = styled.span`
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: #2929ff;
|
||||
color: white;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 10px;
|
||||
|
||||
svg {
|
||||
height: 11px !important;
|
||||
margin-right: 5px;
|
||||
|
||||
&.fa-clock {
|
||||
height: 10px !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const SecondaryBadge = styled(PrimaryBadge)`
|
||||
background: #696969;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
export const InfoBadge = styled(PrimaryBadge)`
|
||||
background: #039640;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
export const DarkBadge = styled(PrimaryBadge)`
|
||||
background: #101010;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
export const BadgeLink = styled.a`
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration:none;
|
||||
}
|
||||
`;
|
||||
133
components/detailed-roadmap/index.js
Normal file
133
components/detailed-roadmap/index.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { useState } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFacebookSquare, faTwitterSquare, faRedditSquare, faGithubSquare } from '@fortawesome/free-brands-svg-icons'
|
||||
import {
|
||||
PageHeader,
|
||||
RoadmapMeta,
|
||||
ShareRoadmap,
|
||||
Sidebar,
|
||||
Summary,
|
||||
SummaryContainer,
|
||||
MobileNavHeader,
|
||||
SidebarButton,
|
||||
MobileSidebar,
|
||||
MobileSidebarWrap,
|
||||
DesktopSidebarWrap,
|
||||
PageTitle,
|
||||
PageDetail
|
||||
} from './style';
|
||||
import { faBars } from '@fortawesome/free-solid-svg-icons'
|
||||
import { getFacebookShareUrl } from 'lib/url';
|
||||
import { ShareIcon } from 'components/share-icon';
|
||||
import { getRedditShareUrl, getTwitterShareUrl } from 'lib/url';
|
||||
import siteConfig from "content/site";
|
||||
import MdRenderer from 'components/md-renderer';
|
||||
|
||||
const DetailedRoadmap = ({ roadmap }) => {
|
||||
const [menuActive, setMenuState] = useState(false);
|
||||
const {
|
||||
sidebar = {},
|
||||
page: currentPage = {},
|
||||
author = {}
|
||||
} = roadmap;
|
||||
|
||||
const roadmapPages = Object.keys(sidebar || {}).map((groupTitle, groupCounter) => {
|
||||
if (groupTitle.startsWith('_')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo remove it after completing the frontend roadmap
|
||||
const isInProgress = groupCounter !== 0;
|
||||
|
||||
return (
|
||||
<div className={`links-group ${isInProgress ? 'in-progress' : ''}`} key={groupTitle}>
|
||||
<h3>
|
||||
{ groupTitle }
|
||||
{ isInProgress && <span className='badge badge-warning progress-badge'>In Progress</span> }
|
||||
</h3>
|
||||
<ul>
|
||||
{ sidebar[groupTitle].map(page => {
|
||||
const isActivePage = page.url === currentPage.url;
|
||||
// e.g. /frontend should mark `/frontend/landscape` as active
|
||||
const isSummaryPage = page.url === `${currentPage.url}/summary`;
|
||||
|
||||
return (
|
||||
<li className={classNames({ active: isActivePage || isSummaryPage })} key={page.url}>
|
||||
<a href={ page.url }>
|
||||
<span className="bullet"></span>
|
||||
{ page.title }
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}) }
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const filePath = currentPage.path.replace(/^\//, '');
|
||||
const RoadmapContent = require(`../../content/${filePath}`).default;
|
||||
|
||||
return (
|
||||
<SummaryContainer>
|
||||
<PageHeader className="border-top border-bottom text-center text-md-left">
|
||||
<div className="container d-flex align-items-center flex-column flex-md-row">
|
||||
<RoadmapMeta>
|
||||
<h3>{ roadmap.title }</h3>
|
||||
<p>
|
||||
Roadmap contributed by <a href={ author.url } target="_blank">{ author.name }</a>
|
||||
{ roadmap.contributorsCount > 1 && ` and <a href="${roadmap.contributorsUrl}">${roadmap.contributorsCount} others</a>`}
|
||||
</p>
|
||||
</RoadmapMeta>
|
||||
<ShareRoadmap className="mt-2 mt-md-0">
|
||||
<ShareIcon href={ siteConfig.url.repo } target="_blank">
|
||||
<FontAwesomeIcon icon={ faGithubSquare } />
|
||||
</ShareIcon>
|
||||
<ShareIcon href={ getFacebookShareUrl({ text: roadmap.title, url: roadmap.url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={ faFacebookSquare } />
|
||||
</ShareIcon>
|
||||
<ShareIcon href={ getTwitterShareUrl({ text: roadmap.title, url: roadmap.url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={ faTwitterSquare } />
|
||||
</ShareIcon>
|
||||
<ShareIcon href={ getRedditShareUrl({ text: roadmap.title, url: roadmap.url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={ faRedditSquare } />
|
||||
</ShareIcon>
|
||||
</ShareRoadmap>
|
||||
</div>
|
||||
</PageHeader>
|
||||
|
||||
<MobileNavHeader className="border-bottom d-block d-md-none">
|
||||
<div className="container">
|
||||
<SidebarButton onClick={() => setMenuState((prevMenuActive) => !prevMenuActive)}>
|
||||
<FontAwesomeIcon icon={ faBars } />
|
||||
{ currentPage.title }
|
||||
</SidebarButton>
|
||||
</div>
|
||||
<MobileSidebarWrap className={classNames({ visible: menuActive })}>
|
||||
<div className="container">
|
||||
<MobileSidebar>
|
||||
{ roadmapPages }
|
||||
</MobileSidebar>
|
||||
</div>
|
||||
</MobileSidebarWrap>
|
||||
</MobileNavHeader>
|
||||
|
||||
<Summary className="container">
|
||||
<DesktopSidebarWrap className="d-none d-md-block">
|
||||
<Sidebar>
|
||||
{ roadmapPages }
|
||||
</Sidebar>
|
||||
</DesktopSidebarWrap>
|
||||
<PageDetail>
|
||||
<PageTitle>{ currentPage.title }</PageTitle>
|
||||
<MdRenderer>
|
||||
<RoadmapContent />
|
||||
</MdRenderer>
|
||||
</PageDetail>
|
||||
</Summary>
|
||||
</SummaryContainer>
|
||||
)
|
||||
};
|
||||
|
||||
export default DetailedRoadmap;
|
||||
199
components/detailed-roadmap/style.js
Normal file
199
components/detailed-roadmap/style.js
Normal file
@@ -0,0 +1,199 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SummaryContainer = styled.div``;
|
||||
|
||||
export const Summary = styled.div`
|
||||
text-align: left;
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
export const PageHeader = styled.div`
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 4px;
|
||||
font-weight: 600;
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-size: 14px;
|
||||
color: #696969;
|
||||
|
||||
a {
|
||||
color: #101010;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const RoadmapMeta = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export const ShareRoadmap = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
margin-bottom: 0;
|
||||
& + a {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MobileNavHeader = styled.div`
|
||||
padding: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
export const SidebarButton = styled.button`
|
||||
background: transparent;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
outline: none !important;
|
||||
-webkit-appearance: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-right: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const PageDetail = styled.div`
|
||||
padding: 25px 0 100px;
|
||||
`;
|
||||
|
||||
export const PageTitle = styled.h1`
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
export const Sidebar = styled.div`
|
||||
padding-bottom: 150px;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.bullet {
|
||||
display: inline-block;
|
||||
margin-right: 7px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
min-width: 7px;
|
||||
border-radius: 100%;
|
||||
background: #efefef;
|
||||
transform: translateX(-4px);
|
||||
transition: background 0.5s ease;
|
||||
}
|
||||
|
||||
|
||||
.links-group {
|
||||
padding: 30px 0 10px;
|
||||
width: 260px;
|
||||
|
||||
h3 {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.progress-badge {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.links-group.in-progress {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.links-group li {
|
||||
list-style: none;
|
||||
margin: 7px 0;
|
||||
|
||||
a {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
color: #696969;
|
||||
}
|
||||
|
||||
.bullet {
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
min-width: 7px;
|
||||
border-radius: 100%;
|
||||
background: #efefef;
|
||||
transform: translateX(-4px);
|
||||
transition: background 0.5s ease;
|
||||
}
|
||||
|
||||
&.active a {
|
||||
color: #101010;
|
||||
}
|
||||
|
||||
&.active .bullet {
|
||||
background: #101010;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DesktopSidebarWrap = styled.div`
|
||||
border-left: 1px solid #efefef;
|
||||
|
||||
${Sidebar} {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
}
|
||||
`;
|
||||
|
||||
export const MobileSidebarWrap = styled.div`
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
background: white;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
bottom: 100%;
|
||||
overflow-y: scroll;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0 10px 20px;
|
||||
top: calc(100% + 1px);
|
||||
transition: bottom 0.5s ease 0s;
|
||||
|
||||
&.visible {
|
||||
bottom: -50vh;
|
||||
}
|
||||
`;
|
||||
|
||||
export const MobileSidebar = styled(Sidebar)`
|
||||
border-left: 1px solid #efefef;
|
||||
margin-left: 12px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.links-group {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.progress-badge {
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
`;
|
||||
16
components/faq-list/index.js
Normal file
16
components/faq-list/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { FaqContainer } from './style';
|
||||
import MdRenderer from 'components/md-renderer';
|
||||
|
||||
const AboutPage = require(`../../content/pages/about.md`).default;
|
||||
|
||||
const FaqList = () => (
|
||||
<FaqContainer className='border-top bg-light'>
|
||||
<div className="container container-small">
|
||||
<MdRenderer>
|
||||
<AboutPage />
|
||||
</MdRenderer>
|
||||
</div>
|
||||
</FaqContainer>
|
||||
);
|
||||
|
||||
export default FaqList;
|
||||
25
components/faq-list/style.js
Normal file
25
components/faq-list/style.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FaqContainer = styled.div`
|
||||
padding: 40px 0;
|
||||
|
||||
h4 {
|
||||
margin-top: 30px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
h4:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
}
|
||||
`;
|
||||
25
components/featured-content/guides.js
Normal file
25
components/featured-content/guides.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import Link from 'next/link';
|
||||
import { FeaturedContentWrap } from './style';
|
||||
import FeaturedGuide from 'components/featured-guide';
|
||||
import { getFeaturedGuides } from 'lib/guide';
|
||||
|
||||
const FeaturedGuides = () => (
|
||||
<FeaturedContentWrap className="featured-content-wrap">
|
||||
<div className="container">
|
||||
<p className='border-through featured-separator'>
|
||||
<span>
|
||||
Guides mostly visited by the community
|
||||
<a href="/guides" className="dark-link d-none d-sm-none d-md-inline d-xl-inline">View all Guides →</a>
|
||||
</span>
|
||||
</p>
|
||||
<div className="swim-lane row">
|
||||
{ getFeaturedGuides()
|
||||
.map(guide => (
|
||||
<FeaturedGuide guide={ guide } key={ guide.url } />
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
</FeaturedContentWrap>
|
||||
);
|
||||
|
||||
export default FeaturedGuides;
|
||||
16
components/featured-content/index.js
Normal file
16
components/featured-content/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { FeaturedWrap } from './style';
|
||||
import FeaturedGuides from './guides';
|
||||
import FeaturedRoadmaps from './roadmaps';
|
||||
|
||||
const FeaturedContent = (props) => (
|
||||
<FeaturedWrap className="border-top bg-light">
|
||||
<FeaturedRoadmaps />
|
||||
<FeaturedGuides />
|
||||
</FeaturedWrap>
|
||||
);
|
||||
|
||||
FeaturedContent.defaultProps = {
|
||||
className: '',
|
||||
};
|
||||
|
||||
export default FeaturedContent;
|
||||
27
components/featured-content/roadmaps.js
Normal file
27
components/featured-content/roadmaps.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import { FeaturedContentWrap } from './style';
|
||||
import roadmaps from 'content/roadmaps';
|
||||
import FeaturedRoadmap from 'components/featured-roadmap';
|
||||
|
||||
const FeaturedRoadmaps = () => (
|
||||
<FeaturedContentWrap className="featured-content-wrap">
|
||||
<div className="container">
|
||||
<div className="featured-head">
|
||||
<p className="border-through featured-separator">
|
||||
<span>
|
||||
Roadmaps mostly visited by the community
|
||||
<a href='/roadmaps' className="dark-link d-none d-sm-none d-md-inline d-xl-inline">View all Roadmaps →</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="swim-lane row">
|
||||
{ roadmaps
|
||||
.filter(({ featured }) => featured)
|
||||
.map(roadmap => (
|
||||
<FeaturedRoadmap roadmap={ roadmap } key={ roadmap.url } />
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
</FeaturedContentWrap>
|
||||
);
|
||||
|
||||
export default FeaturedRoadmaps;
|
||||
110
components/featured-content/style.js
Normal file
110
components/featured-content/style.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FeaturedWrap = styled.div`
|
||||
padding: 30px 0 50px;
|
||||
`;
|
||||
|
||||
export const FeaturedContentWrap = styled.div`
|
||||
.featured-head {
|
||||
text-align: center;
|
||||
display: block;
|
||||
|
||||
h3 {
|
||||
font-weight: 700;
|
||||
font-size: 35px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.featured-separator {
|
||||
font-size: 16px;
|
||||
margin-bottom: 40px;
|
||||
margin-top: 25px;
|
||||
color: #999;
|
||||
|
||||
span {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
a {
|
||||
background: black;
|
||||
color: white;
|
||||
padding: 3px 8px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.swim-lane {
|
||||
.featured-block {
|
||||
}
|
||||
}
|
||||
|
||||
.guide-item {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
padding: 15px 10px;
|
||||
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
font-size: 15px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.journey-block {
|
||||
a {
|
||||
border: 1px solid #f7f7f7;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
background: #ffffff;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
|
||||
transition: box-shadow 0.2s ease 0s;
|
||||
cursor: pointer;
|
||||
margin-bottom: 32px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
|
||||
|
||||
img {
|
||||
filter: grayscale(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.journey-meta {
|
||||
padding: 18px 25px 20px;
|
||||
|
||||
h4 {
|
||||
line-height: 27px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0;
|
||||
font-size: 18px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
line-height: 25px;
|
||||
color: #999999;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
filter: grayscale(1);
|
||||
border-radius: 10px 10px 0 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
25
components/featured-guide/index.js
Normal file
25
components/featured-guide/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import formatDate from 'date-fns/format';
|
||||
|
||||
import { Author, AuthorImage, AuthorName, BlockLink, BlockMeta, BlockSubtitle, BlockTitle, PublishDate } from './style';
|
||||
import { findByUsername } from 'lib/author';
|
||||
|
||||
const FeaturedGuide = ({ guide }) => {
|
||||
const author = findByUsername(guide.author) || {};
|
||||
return (
|
||||
<div className='col-xl-4 col-lg-6 col-md-6 col-sm-12 col-12 grid-item-container'>
|
||||
<BlockLink href={guide.url}>
|
||||
<BlockTitle>{guide.title}</BlockTitle>
|
||||
<BlockSubtitle>{guide.featuredDescription || guide.description}</BlockSubtitle>
|
||||
<BlockMeta>
|
||||
<Author>
|
||||
<AuthorImage src={author.picture} />
|
||||
<AuthorName>{author.name}</AuthorName>
|
||||
</Author>
|
||||
<PublishDate>{formatDate(new Date(guide.createdAt), 'MMMM d, yyyy')}</PublishDate>
|
||||
</BlockMeta>
|
||||
</BlockLink>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeaturedGuide;
|
||||
65
components/featured-guide/style.js
Normal file
65
components/featured-guide/style.js
Normal file
@@ -0,0 +1,65 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const BlockLink = styled.a`
|
||||
border: 1px solid #f7f7f7;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
background: #ffffff;
|
||||
padding: 25px 25px 22px;
|
||||
border-radius: 10px;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
|
||||
transition: box-shadow 0.2s ease 0s;
|
||||
cursor: pointer;
|
||||
margin-bottom: 32px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const BlockTitle = styled.h4`
|
||||
line-height: 27px;
|
||||
font-weight: 600;
|
||||
font-size: 22px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const BlockSubtitle = styled.p`
|
||||
font-size: 15px;
|
||||
line-height: 25px;
|
||||
color: #999999;
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
export const BlockMeta = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 19px;
|
||||
`;
|
||||
|
||||
export const PublishDate = styled.time`
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
`;
|
||||
|
||||
export const Author = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 15px;
|
||||
color: #999;
|
||||
`;
|
||||
|
||||
export const AuthorImage = styled.img`
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
border-radius: 100%;
|
||||
margin-right: 10px;
|
||||
`;
|
||||
export const AuthorName = styled.div``;
|
||||
12
components/featured-roadmap/index.js
Normal file
12
components/featured-roadmap/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { BlockLink, BlockSubtitle, BlockTitle } from './style';
|
||||
|
||||
const FeaturedRoadmap = ({ roadmap }) => (
|
||||
<div className='col-xl-4 col-lg-4 col-md-6 col-sm-12 col-12 grid-item-container'>
|
||||
<BlockLink href={roadmap.url}>
|
||||
<BlockTitle>{roadmap.title}</BlockTitle>
|
||||
<BlockSubtitle>{roadmap.featuredDescription || roadmap.description}</BlockSubtitle>
|
||||
</BlockLink>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default FeaturedRoadmap;
|
||||
38
components/featured-roadmap/style.js
Normal file
38
components/featured-roadmap/style.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const BlockLink = styled.a`
|
||||
border: 1px solid #f7f7f7;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
background: #ffffff;
|
||||
padding: 26px 25px 25px;
|
||||
border-radius: 10px;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
|
||||
transition: box-shadow 0.2s ease 0s;
|
||||
cursor: pointer;
|
||||
margin-bottom: 32px;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: #000000;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const BlockTitle = styled.h4`
|
||||
line-height: 27px;
|
||||
font-weight: 600;
|
||||
font-size: 22px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const BlockSubtitle = styled.p`
|
||||
font-size: 15px;
|
||||
line-height: 25px;
|
||||
color: #999999;
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
23
components/guide-body/index.js
Normal file
23
components/guide-body/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import MdRenderer from 'components/md-renderer'
|
||||
import SharePage from 'components/share-page';
|
||||
import { GuideBodyWrap } from './style';
|
||||
|
||||
const GuideBody = ({ guide }) => {
|
||||
const GuideContent = require(`../../content/guides/${guide.fileName}.md`).default;
|
||||
return (
|
||||
<GuideBodyWrap>
|
||||
<MdRenderer>
|
||||
<GuideContent />
|
||||
{
|
||||
guide.author && <SharePage
|
||||
title={ guide.title }
|
||||
url={ guide.url }
|
||||
twitterUsername={ guide.author.twitter }
|
||||
/>
|
||||
}
|
||||
</MdRenderer>
|
||||
</GuideBodyWrap>
|
||||
);
|
||||
};
|
||||
|
||||
export default GuideBody;
|
||||
13
components/guide-body/style.js
Normal file
13
components/guide-body/style.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const GuideBodyWrap = styled.div`
|
||||
position: relative;
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
min-height: 300px;
|
||||
|
||||
p:first-child, h1, h2, h3, h4, h5, h6, img, blockquote {
|
||||
margin-top: 0;
|
||||
}
|
||||
`;
|
||||
83
components/guide-footer/index.js
Normal file
83
components/guide-footer/index.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFacebookSquare, faGithub, faHackerNewsSquare, faRedditSquare, faTwitter, faTwitterSquare } from '@fortawesome/free-brands-svg-icons'
|
||||
import { getContributionUrl } from "lib/guide";
|
||||
import {
|
||||
getTwitterUrl,
|
||||
getTwitterShareUrl,
|
||||
getFacebookShareUrl,
|
||||
getRedditShareUrl,
|
||||
getHnShareUrl
|
||||
} from "lib/url";
|
||||
import {
|
||||
AuthorBio,
|
||||
AuthorImg,
|
||||
AuthorInfoWrap,
|
||||
AuthorMeta,
|
||||
ContributeIcon,
|
||||
FooterBg,
|
||||
FooterContainer,
|
||||
FooterWrap,
|
||||
ShareIcons,
|
||||
ShareWrap
|
||||
} from './style';
|
||||
|
||||
|
||||
const GuideFooter = ({
|
||||
guide,
|
||||
guide: {
|
||||
author = {}
|
||||
} = {}
|
||||
}) => (
|
||||
<FooterWrap>
|
||||
<FooterBg className="border-top">
|
||||
<FooterContainer>
|
||||
<ShareWrap>
|
||||
<ContributeIcon>
|
||||
<a href={ getContributionUrl(guide) } target="_blank">
|
||||
<span className="d-none d-sm-none d-md-inline d-lg-inline d-xl-inline">Improve this Guide </span>
|
||||
<span className="d-inline d-sm-inline d-md-none d-lg-none d-xl-none">Contribute </span>
|
||||
<FontAwesomeIcon icon={faGithub}/>
|
||||
</a>
|
||||
</ContributeIcon>
|
||||
<ContributeIcon hasMargins>
|
||||
<a href={ getTwitterUrl(author.twitter) } target="_blank">
|
||||
<span className="d-none d-sm-none d-md-inline d-lg-inline d-xl-inline">Follow the author </span>
|
||||
<span className="d-inline d-sm-inline d-md-none d-lg-none d-xl-none">Author </span>
|
||||
<FontAwesomeIcon icon={faTwitter}/>
|
||||
</a>
|
||||
</ContributeIcon>
|
||||
<ShareIcons>
|
||||
<span className="d-none d-sm-none d-md-none d-lg-inline d-xl-inline">Help spread the word</span>
|
||||
<span className="d-inline d-sm-inline d-md-inline d-lg-none d-xl-none">Share</span>
|
||||
<a href={ getTwitterShareUrl({ text: `${guide.title} by @${author.twitter}`, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faTwitterSquare}/>
|
||||
</a>
|
||||
<a href={ getFacebookShareUrl({ text: guide.title, url: guide.url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={faFacebookSquare}/>
|
||||
</a>
|
||||
<a href={ getRedditShareUrl({ text: guide.title, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faRedditSquare}/>
|
||||
</a>
|
||||
<a href={ getHnShareUrl({ text: guide.title, url: guide.url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faHackerNewsSquare}/>
|
||||
</a>
|
||||
</ShareIcons>
|
||||
</ShareWrap>
|
||||
</FooterContainer>
|
||||
</FooterBg>
|
||||
|
||||
<FooterBg className="border-top">
|
||||
<FooterContainer>
|
||||
<AuthorInfoWrap>
|
||||
<AuthorImg src={ author.picture } alt={ author.name }/>
|
||||
<AuthorMeta>
|
||||
<h4><a href={ getTwitterUrl(author.twitter) } target="_blank">{ author.name }</a></h4>
|
||||
<AuthorBio>{ author.bio }</AuthorBio>
|
||||
</AuthorMeta>
|
||||
</AuthorInfoWrap>
|
||||
</FooterContainer>
|
||||
</FooterBg>
|
||||
</FooterWrap>
|
||||
);
|
||||
|
||||
export default GuideFooter;
|
||||
123
components/guide-footer/style.js
Normal file
123
components/guide-footer/style.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FooterWrap = styled.div`
|
||||
display: block;
|
||||
margin-top: 50px;
|
||||
`;
|
||||
|
||||
export const FooterContainer = styled.div`
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
`;
|
||||
|
||||
export const FooterBg = styled.div`
|
||||
`;
|
||||
|
||||
export const ShareWrap = styled.div`
|
||||
padding: 17px 0px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #101010;
|
||||
|
||||
svg {
|
||||
height: 18px !important;
|
||||
width: 18px !important;
|
||||
color: #757575;
|
||||
margin-left: 7px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
color: #101010;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
export const ContributeIcon = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: ${props => props.hasMargins ? '0 30px' : '0'};
|
||||
|
||||
span {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #757575;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: #101010;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const ShareIcons = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #757575;
|
||||
font-size: 14px;
|
||||
|
||||
span {
|
||||
margin-right: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const AuthorInfoWrap = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 40px 0;
|
||||
|
||||
h4 {
|
||||
position: relative;
|
||||
font-size: 22px;
|
||||
margin: 10px 0;
|
||||
line-height: 17px;
|
||||
|
||||
a {
|
||||
font-weight: 700;
|
||||
color: #101010;
|
||||
}
|
||||
}
|
||||
|
||||
.author-description {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const AuthorBio = styled.p`
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
color: #757575;
|
||||
margin-bottom: 0;
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
|
||||
export const AuthorImg = styled.img`
|
||||
border-radius: 100%;
|
||||
height: 100px !important;
|
||||
width: 100px !important;
|
||||
margin-right: 22px;
|
||||
display: block;
|
||||
`;
|
||||
|
||||
export const AuthorMeta = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
`;
|
||||
57
components/guide-header/index.js
Normal file
57
components/guide-header/index.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import formatDate from 'date-fns/format'
|
||||
import {
|
||||
ActionItems,
|
||||
AuthorImage,
|
||||
EditGuide,
|
||||
GuideAuthor,
|
||||
GuideDate,
|
||||
GuideMeta,
|
||||
GuideSubtitle,
|
||||
GuideTitle,
|
||||
HeaderWrap,
|
||||
} from './style';
|
||||
import { getContributionUrl } from "lib/guide";
|
||||
import { getTwitterUrl } from "lib/url";
|
||||
import { faClock, faEnvelope, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import { BadgeLink, BadgesList, PrimaryBadge, SecondaryBadge, DarkBadge } from 'components/badges';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
const GuideHeader = ({
|
||||
guide,
|
||||
guide: {
|
||||
author = {}
|
||||
} = {}
|
||||
}) => (
|
||||
<HeaderWrap className="border-bottom">
|
||||
<GuideMeta>
|
||||
<GuideAuthor href={ getTwitterUrl(author.twitter) } target="_blank">
|
||||
<AuthorImage src={ author.picture } />
|
||||
{ author.name }
|
||||
</GuideAuthor>
|
||||
·
|
||||
<GuideDate>{ formatDate(new Date(guide.createdAt), 'EEEE, MMMM d yyyy') }</GuideDate>
|
||||
·
|
||||
<EditGuide href={ getContributionUrl(guide) } target="_blank">Improve this Guide</EditGuide>
|
||||
</GuideMeta>
|
||||
<GuideTitle>{ guide.title }</GuideTitle>
|
||||
<GuideSubtitle>{ guide.description }</GuideSubtitle>
|
||||
<ActionItems>
|
||||
<BadgesList className="mt-4">
|
||||
<BadgeLink href="/guides">
|
||||
<SecondaryBadge>
|
||||
<FontAwesomeIcon icon={faArrowLeft}/>
|
||||
Other Guides
|
||||
</SecondaryBadge>
|
||||
</BadgeLink>
|
||||
<BadgeLink href="/signup">
|
||||
<PrimaryBadge>
|
||||
<FontAwesomeIcon icon={faEnvelope}/>
|
||||
Send me Updates
|
||||
</PrimaryBadge>
|
||||
</BadgeLink>
|
||||
</BadgesList>
|
||||
</ActionItems>
|
||||
</HeaderWrap>
|
||||
);
|
||||
|
||||
export default GuideHeader;
|
||||
54
components/guide-header/style.js
Normal file
54
components/guide-header/style.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const HeaderWrap = styled.div`
|
||||
padding: 80px 15px 45px;
|
||||
text-align: center;
|
||||
margin-bottom: 35px;
|
||||
`;
|
||||
|
||||
export const GuideTitle = styled.h1`
|
||||
font-weight: 700;
|
||||
font-size: 46px;
|
||||
margin: 12px 0;
|
||||
`;
|
||||
|
||||
export const GuideSubtitle = styled.p`
|
||||
margin-bottom: 0;
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
`;
|
||||
|
||||
export const GuideMeta = styled.p`
|
||||
margin-bottom: 0;
|
||||
color: #757575;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
`;
|
||||
|
||||
export const GuideDate = styled.span`
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
`;
|
||||
|
||||
export const GuideAuthor = styled.a`
|
||||
margin-right: 7px;
|
||||
font-weight: 500;
|
||||
color: #101010;
|
||||
|
||||
&:hover {
|
||||
color: #101010;
|
||||
}
|
||||
`;
|
||||
|
||||
export const AuthorImage = styled.img`
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 100%;
|
||||
margin-right: 10px;
|
||||
`;
|
||||
|
||||
export const EditGuide = styled.a`
|
||||
margin-left: 7px;
|
||||
`;
|
||||
|
||||
export const ActionItems = styled.div``;
|
||||
74
components/helmet/index.js
Normal file
74
components/helmet/index.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import NextHead from 'next/head';
|
||||
import siteConfig from 'content/site';
|
||||
|
||||
const prepareTitle = (givenTitle) => {
|
||||
return givenTitle || siteConfig.title;
|
||||
};
|
||||
|
||||
const prepareDescription = (givenDescription) => {
|
||||
return givenDescription || siteConfig.description;
|
||||
};
|
||||
|
||||
// noinspection JSUnresolvedLibraryURL
|
||||
const Helmet = (props) => (
|
||||
<NextHead>
|
||||
<meta charSet='UTF-8' />
|
||||
|
||||
<title>{ prepareTitle(props.title) }</title>
|
||||
<meta name='description' content={ prepareDescription(props.description) } />
|
||||
|
||||
<meta name="author" content={ siteConfig.author } />
|
||||
<meta name="keywords" content={ props.keywords ? props.keywords.join(',') : siteConfig.keywords.join(',') } />
|
||||
|
||||
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1.0, maximum-scale=3.0, minimum-scale=1.0" />
|
||||
{ props.canonical && <link rel="canonical" href={ props.canonical } /> }
|
||||
<meta httpEquiv="Content-Language" content="en" />
|
||||
|
||||
<meta property="og:title" content={ prepareTitle(props.title) } />
|
||||
<meta property="og:description" content={ prepareDescription(props.description) } />
|
||||
<meta property="og:image" content={ `${siteConfig.url.web}${siteConfig.logoSquare}` } />
|
||||
<meta property="og:url" content={ siteConfig.url.web } />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="article:publisher" content={ `https://facebook.com/${siteConfig.facebook}` } />
|
||||
<meta property="og:site_name" content={ siteConfig.name } />
|
||||
<meta property="article:author" content={ siteConfig.author } />
|
||||
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:site" content={ `@${siteConfig.twitter}` } />
|
||||
<meta name="twitter:title" content={ prepareTitle(props.title) } />
|
||||
<meta name="twitter:description" content={ prepareDescription(props.description) } />
|
||||
<meta name="twitter:image" content={ `${siteConfig.url.web}${siteConfig.logoSquare}` } />
|
||||
<meta name="twitter:image:alt" content="roadmap.sh" />
|
||||
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/manifest/apple-touch-icon.png" />
|
||||
<meta name="msapplication-TileColor" content="#101010" />
|
||||
<meta name="theme-color" content="#848a9a" />
|
||||
|
||||
<link rel="manifest" href="/manifest/manifest.json" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/manifest/icon32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/manifest/icon16.png" />
|
||||
<link rel="shortcut icon" href="/manifest/favicon.ico" type="image/x-icon" />
|
||||
<link rel="icon" href="/manifest/favicon.ico" type="image/x-icon" />
|
||||
|
||||
{ /* Global Site Tag (gtag.js) - Google Analytics */ }
|
||||
{ process.env.GA_SECRET && (
|
||||
<>
|
||||
<script async src={ `https://www.googletagmanager.com/gtag/js?id=${process.env.GA_SECRET}` } />
|
||||
<script dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', '${process.env.GA_SECRET}');
|
||||
`,
|
||||
}} />
|
||||
</>
|
||||
)}
|
||||
|
||||
</NextHead>
|
||||
);
|
||||
|
||||
export default Helmet;
|
||||
13
components/hero-section/index.js
Normal file
13
components/hero-section/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import Link from 'next/link';
|
||||
import { HeroSectionWrap } from './style';
|
||||
|
||||
const HeroSection = () => (
|
||||
<HeroSectionWrap>
|
||||
<div className="container">
|
||||
<h1>Developer Roadmaps</h1>
|
||||
<p>Community driven roadmaps, articles and resources for developers. <Link href="/signup"><a>Sign up</a></Link> for occasional updates on new roadmaps, updates and guides</p>
|
||||
</div>
|
||||
</HeroSectionWrap>
|
||||
);
|
||||
|
||||
export default HeroSection;
|
||||
30
components/hero-section/style.js
Normal file
30
components/hero-section/style.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const HeroSectionWrap = styled.div`
|
||||
text-align: center;
|
||||
padding: 70px 20px;
|
||||
margin: 0 auto;
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 70px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
font-weight: 400;
|
||||
margin-bottom: 0;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
}
|
||||
`;
|
||||
15
components/icon-row-block/index.js
Normal file
15
components/icon-row-block/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import RowBlock from 'components/row-block';
|
||||
import { SubmitText, SubmitWrap } from './style';
|
||||
|
||||
const IconRowBlock = ({ url, icon, text, openExternal=false }) => (
|
||||
<RowBlock url={ url } openExternal={openExternal}>
|
||||
<SubmitWrap>
|
||||
<FontAwesomeIcon icon={ icon } />
|
||||
<SubmitText>{ text }</SubmitText>
|
||||
</SubmitWrap>
|
||||
</RowBlock>
|
||||
);
|
||||
|
||||
export default IconRowBlock;
|
||||
20
components/icon-row-block/style.js
Normal file
20
components/icon-row-block/style.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SubmitWrap = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
svg {
|
||||
height: 25px;
|
||||
color: #d6d6d6;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SubmitText = styled.p`
|
||||
margin-bottom: 0;
|
||||
color: dimgrey;
|
||||
margin-top: 8px;
|
||||
`;
|
||||
3
components/icons/facebook.svg
Normal file
3
components/icons/facebook.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="29" height="29">
|
||||
<path d="M23.2 5H5.8a.8.8 0 0 0-.8.8V23.2c0 .44.35.8.8.8h9.3v-7.13h-2.38V13.9h2.38v-2.38c0-2.45 1.55-3.66 3.74-3.66 1.05 0 1.95.08 2.2.11v2.57h-1.5c-1.2 0-1.48.57-1.48 1.4v1.96h2.97l-.6 2.97h-2.37l.05 7.12h5.1a.8.8 0 0 0 .79-.8V5.8a.8.8 0 0 0-.8-.79"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 298 B |
4
components/icons/github.svg
Normal file
4
components/icons/github.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" aria-label="GitHub" role="img" viewBox="0 0 512 512" width="22px" height="20px">
|
||||
<rect width="512" height="512" rx="15%"/>
|
||||
<path fill="#fff" d="M335 499c14 0 12 17 12 17H165s-2-17 12-17c13 0 16-6 16-12l-1-50c-71 16-86-28-86-28-12-30-28-37-28-37-24-16 1-16 1-16 26 2 40 26 40 26 22 39 59 28 74 22 2-17 9-28 16-35-57-6-116-28-116-126 0-28 10-51 26-69-3-6-11-32 3-67 0 0 21-7 70 26 42-12 86-12 128 0 49-33 70-26 70-26 14 35 6 61 3 67 16 18 26 41 26 69 0 98-60 120-117 126 10 8 18 24 18 48l-1 70c0 6 3 12 16 12z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 565 B |
4
components/icons/link.svg
Normal file
4
components/icons/link.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 474 B |
3
components/icons/twitter.svg
Normal file
3
components/icons/twitter.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="29" height="29">
|
||||
<path d="M22.05 7.54a4.47 4.47 0 0 0-3.3-1.46 4.53 4.53 0 0 0-4.53 4.53c0 .35.04.7.08 1.05A12.9 12.9 0 0 1 5 6.89a5.1 5.1 0 0 0-.65 2.26c.03 1.6.83 2.99 2.02 3.79a4.3 4.3 0 0 1-2.02-.57v.08a4.55 4.55 0 0 0 3.63 4.44c-.4.08-.8.13-1.21.16l-.81-.08a4.54 4.54 0 0 0 4.2 3.15 9.56 9.56 0 0 1-5.66 1.94l-1.05-.08c2 1.27 4.38 2.02 6.94 2.02 8.3 0 12.86-6.9 12.84-12.85.02-.24 0-.43 0-.65a8.68 8.68 0 0 0 2.26-2.34c-.82.38-1.7.62-2.6.72a4.37 4.37 0 0 0 1.95-2.51c-.84.53-1.81.9-2.83 1.13z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 529 B |
9
components/link/badge-link.js
Normal file
9
components/link/badge-link.js
Normal file
@@ -0,0 +1,9 @@
|
||||
export function BadgeLink({ target='_blank', variant ='success', badgeText, href, children }) {
|
||||
return (
|
||||
<p className='mb-0'>
|
||||
<a href={href} target={ target }>
|
||||
<span style={{ position: 'relative', top: '-2px'}} className={`badge badge-${variant}`}>{badgeText}</span> { children }
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
13
components/link/index.js
Normal file
13
components/link/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const StrongLink = styled.a`
|
||||
border-bottom: 2px solid currentColor;
|
||||
position: relative;
|
||||
transition: background-color 120ms;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
`;
|
||||
11
components/mark/index.js
Normal file
11
components/mark/index.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Mark = styled.span`
|
||||
background: #1b1e21;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0 4px;
|
||||
padding: 0 6px 0 7px;
|
||||
`;
|
||||
10
components/md-renderer/index.js
Normal file
10
components/md-renderer/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { MDXProvider } from '@mdx-js/react';
|
||||
import MdxComponents from './mdx-components';
|
||||
|
||||
const MdRenderer = (props) => (
|
||||
<MDXProvider components={ MdxComponents }>
|
||||
{ props.children }
|
||||
</MDXProvider>
|
||||
);
|
||||
|
||||
export default MdRenderer;
|
||||
18
components/md-renderer/mdx-components/a.js
Normal file
18
components/md-renderer/mdx-components/a.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Link = styled.a`
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const EnrichedLink = props => {
|
||||
// Is external URL or is a media URL
|
||||
const isExternalUrl = /(^http(s)?:\/\/)|(\.(png|svg|jpeg|jpg)$)/.test(props.href);
|
||||
|
||||
return (
|
||||
<Link href={ props.href } target={ isExternalUrl ? '_blank' : '_self' }>
|
||||
{ props.children }
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnrichedLink;
|
||||
26
components/md-renderer/mdx-components/blockquote.js
Normal file
26
components/md-renderer/mdx-components/blockquote.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const BlockQuote = styled.blockquote`
|
||||
padding: 16px 20px;
|
||||
position: relative;
|
||||
background: #e8e8e8;
|
||||
border-radius: 5px;
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p + h4 {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
|
||||
& + p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default BlockQuote;
|
||||
77
components/md-renderer/mdx-components/heading.js
Normal file
77
components/md-renderer/mdx-components/heading.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import styled from 'styled-components';
|
||||
import Link from 'components/icons/link.svg';
|
||||
|
||||
const linkify = (Component) => {
|
||||
return (props) => {
|
||||
const text = props.children;
|
||||
const id = text.toLowerCase && text
|
||||
.toLowerCase()
|
||||
.replace(/[^\x00-\x7F]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[?!]/g, '');
|
||||
|
||||
return (
|
||||
<Component id={ id }>
|
||||
<HeaderLink href={ `#${id}` }>
|
||||
<Link />
|
||||
</HeaderLink>
|
||||
{ props.children }
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
const HeaderLink = styled.a`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -25px;
|
||||
width: 25px;
|
||||
display: none;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
const H1 = styled.h1`
|
||||
position: relative;
|
||||
font-size: 42px;
|
||||
font-weight: 700;
|
||||
margin: 32px 0 10px !important;
|
||||
|
||||
&:hover ${HeaderLink} {
|
||||
display: flex;
|
||||
}
|
||||
`;
|
||||
|
||||
const H2 = styled(H1).attrs({ as: 'h2' })`
|
||||
font-size: 32px;
|
||||
`;
|
||||
|
||||
const H3 = styled(H1).attrs({ as: 'h3' })`
|
||||
margin: 22px 0 8px;
|
||||
font-size: 30px;
|
||||
`;
|
||||
|
||||
const H4 = styled(H1).attrs({ as: 'h4' })`
|
||||
margin: 18px 0 8px;
|
||||
font-size: 24px;
|
||||
`;
|
||||
|
||||
const H5 = styled(H1).attrs({ as: 'h5' })`
|
||||
margin: 14px 0 8px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const H6 = styled(H1).attrs({ as: 'h6' })`
|
||||
margin: 12px 0 8px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
export const Headings = {
|
||||
h1: linkify(H1),
|
||||
h2: linkify(H2),
|
||||
h3: linkify(H3),
|
||||
h4: linkify(H4),
|
||||
h5: linkify(H5),
|
||||
h6: linkify(H6),
|
||||
};
|
||||
8
components/md-renderer/mdx-components/iframe.js
Normal file
8
components/md-renderer/mdx-components/iframe.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const IFrame = styled.iframe`
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
margin: 30px auto;
|
||||
`;
|
||||
7
components/md-renderer/mdx-components/img.js
Normal file
7
components/md-renderer/mdx-components/img.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Img = styled.img`
|
||||
max-width: 100%;
|
||||
margin: 25px auto;
|
||||
display: block;
|
||||
`;
|
||||
23
components/md-renderer/mdx-components/index.js
Normal file
23
components/md-renderer/mdx-components/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import P from './p';
|
||||
import { Headings } from './heading';
|
||||
import { Pre } from './pre';
|
||||
import BlockQuote from './blockquote';
|
||||
import { Table } from './table';
|
||||
import { IFrame } from './iframe';
|
||||
import { Img } from './img';
|
||||
import EnrichedLink from './a';
|
||||
import { BadgeLink } from '../../link/badge-link';
|
||||
|
||||
const MdxComponents = {
|
||||
p: P,
|
||||
...Headings,
|
||||
pre: Pre,
|
||||
blockquote: BlockQuote,
|
||||
a: EnrichedLink,
|
||||
table: Table,
|
||||
iframe: IFrame,
|
||||
img: Img,
|
||||
BadgeLink: BadgeLink
|
||||
};
|
||||
|
||||
export default MdxComponents;
|
||||
20
components/md-renderer/mdx-components/p.js
Normal file
20
components/md-renderer/mdx-components/p.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const P = styled.p`
|
||||
color: inherit;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 26px;
|
||||
margin: 0 0 12px;
|
||||
|
||||
img + em {
|
||||
text-align: center;
|
||||
color: #666666;
|
||||
font-style: normal;
|
||||
font-size: 14px;
|
||||
margin: 5px 0 10px;
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
export default P;
|
||||
12
components/md-renderer/mdx-components/pre.js
Normal file
12
components/md-renderer/mdx-components/pre.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Pre = styled.pre`
|
||||
margin: 25px -25px 25px -25px !important;
|
||||
padding: 20px 25px !important;
|
||||
border-radius: 10px;
|
||||
line-height: 1.5 !important;
|
||||
|
||||
code {
|
||||
background: transparent;
|
||||
}
|
||||
`;
|
||||
25
components/md-renderer/mdx-components/table.js
Normal file
25
components/md-renderer/mdx-components/table.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Table = styled.table`
|
||||
border-collapse: separate;
|
||||
width: 100%;
|
||||
border-spacing: 0;
|
||||
margin: 20px 0;
|
||||
|
||||
th {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
background: #FAFAFA;
|
||||
text-transform: uppercase;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #EAEAEA;
|
||||
}
|
||||
`;
|
||||
62
components/page-footer/carbon.scss
Normal file
62
components/page-footer/carbon.scss
Normal file
@@ -0,0 +1,62 @@
|
||||
#carbonads {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu,
|
||||
Cantarell, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
#carbonads {
|
||||
display: flex;
|
||||
max-width: 330px;
|
||||
background-color: hsl(0, 0%, 98%);
|
||||
box-shadow: 0 1px 4px 1px hsla(0, 0%, 0%, .1);
|
||||
}
|
||||
|
||||
#carbonads a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#carbonads a:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#carbonads span {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#carbonads .carbon-wrap {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.carbon-img {
|
||||
display: block;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.carbon-img img {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.carbon-text {
|
||||
font-size: 13px;
|
||||
padding: 10px;
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.carbon-poweredby {
|
||||
display: block;
|
||||
padding: 8px 10px;
|
||||
background: repeating-linear-gradient(-45deg, transparent, transparent 5px, hsla(0, 0%, 0%, .025) 5px, hsla(0, 0%, 0%, .025) 10px) hsla(203, 11%, 95%, .4);
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .5px;
|
||||
font-weight: 600;
|
||||
font-size: 9px;
|
||||
line-height: 1;
|
||||
}
|
||||
41
components/page-footer/custom-ad.jsx
Normal file
41
components/page-footer/custom-ad.jsx
Normal file
@@ -0,0 +1,41 @@
|
||||
export const CustomAd = () => {
|
||||
return (
|
||||
<div id="carbonads">
|
||||
<span>
|
||||
<span className="carbon-wrap">
|
||||
<a
|
||||
href="https://freemote.com/strategy?sl=roadmap"
|
||||
className="carbon-img"
|
||||
target="_blank"
|
||||
rel="noopener sponsored"
|
||||
>
|
||||
<img
|
||||
src="/fm-img.png"
|
||||
alt="FM Logo"
|
||||
border="0"
|
||||
height="100"
|
||||
width="130"
|
||||
style={{ maxWidth: "130px" }}
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://freemote.com/strategy?sl=roadmap"
|
||||
className="carbon-text"
|
||||
target="_blank"
|
||||
rel="noopener sponsored"
|
||||
>
|
||||
He Went from ZERO TO $74,000 as a Full Time Developer in 7 Weeks
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
href="https://github.com/sponsors/kamranahmedse"
|
||||
className="carbon-poweredby"
|
||||
target="_blank"
|
||||
rel="noopener sponsored"
|
||||
>
|
||||
Sponsored by
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
71
components/page-footer/index.js
Normal file
71
components/page-footer/index.js
Normal file
@@ -0,0 +1,71 @@
|
||||
import siteConfig from 'content/site';
|
||||
import { FooterWrap } from './style.js';
|
||||
import './carbon.scss';
|
||||
import { CustomAd } from "./custom-ad";
|
||||
|
||||
const PageFooter = () => (
|
||||
<FooterWrap className="border-top">
|
||||
<div className="container">
|
||||
<div className="foot-cols-wrap row">
|
||||
<div className="site-meta-wrap col-12 col-sm-12 col-lg col-xl col-md-12">
|
||||
<div className="site-meta">
|
||||
<div className="brand-detail">
|
||||
<a href="/" className='brand'><img src="/brand.png" alt="" /> roadmap.sh</a>
|
||||
<span className="preposition">by</span>
|
||||
<a href="https://twitter.com/kamranahmedse" target="_blank" className='follow-author'>@kamranahmedse</a>
|
||||
</div>
|
||||
<div className="brand-explanation">
|
||||
<p>Community created roadmaps, articles, resources and journeys to help you choose your path and grow in your career.</p>
|
||||
</div>
|
||||
<p className='meta-links'>
|
||||
© roadmap.sh ·
|
||||
<a href="/about">FAQ</a> ·
|
||||
<a href="/terms">Terms</a> ·
|
||||
<a href="/privacy">Privacy</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="site-contribute foot-col col-12 col-sm-4 col-lg-2">
|
||||
<ul>
|
||||
<li className='foot-header'>Contribute</li>
|
||||
<li><a href='https://gum.co/roadmap-sh' target="_blank">Sponsor us</a></li>
|
||||
<li><a href={ siteConfig.url.addGuide } target="_blank">Write a Guide</a></li>
|
||||
<li><a href={ siteConfig.url.addRoadmap } target="_blank">Submit a Roadmap</a></li>
|
||||
<li><a href='/about'>About this Site</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="site-learn foot-col col-12 col-sm-4 col-lg-2">
|
||||
<ul>
|
||||
<li className="foot-header">Learn</li>
|
||||
<li><a href="/guides">Read Guides</a></li>
|
||||
<li><a href="/watch">Watch Videos</a></li>
|
||||
<li><a href="/podcasts">Podcasts</a></li>
|
||||
<li><a href="https://www.youtube.com/channel/UCA0H2KIWgWTwpTFjSxp0now?sub_confirmation=1" target="_blank">YouTube</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="site-learn foot-col col-12 col-sm-4 col-lg-2">
|
||||
<ul>
|
||||
<li className="foot-header">Most Visited</li>
|
||||
<li><a href="/frontend">Frontend Roadmap</a></li>
|
||||
<li><a href="/backend">Backend Roadmap</a></li>
|
||||
<li><a href="/devops">DevOps Roadmap</a></li>
|
||||
<li><a href="/roadmaps">Upcoming</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CustomAd />
|
||||
|
||||
{/* Do not show on local */}
|
||||
{ process.env.GA_SECRET && false && (
|
||||
<>
|
||||
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CE7DLK3Y&placement=roadmapsh" id="_carbonads_js"></script>
|
||||
{/*<div id="codefund"></div>*/}
|
||||
{/*<script src="https://app.codefund.io/properties/681/funder.js" async></script>*/}
|
||||
</>
|
||||
) }
|
||||
</FooterWrap>
|
||||
);
|
||||
|
||||
export default PageFooter;
|
||||
94
components/page-footer/style.js
Normal file
94
components/page-footer/style.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const FooterWrap = styled.div`
|
||||
padding: 65px 0;
|
||||
|
||||
.site-meta {
|
||||
margin-bottom: 30px;
|
||||
width: 350px;
|
||||
|
||||
.brand-detail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
|
||||
img {
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.preposition {
|
||||
margin: 0 7px;
|
||||
}
|
||||
|
||||
.follow-author {
|
||||
background-color: #1e99e6;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 20px;
|
||||
padding: 0 6px;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
background: #43aaea;
|
||||
}
|
||||
}
|
||||
|
||||
.brand-explanation {
|
||||
color: #999;
|
||||
|
||||
p {
|
||||
font-size: 15px;
|
||||
line-height: 24px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.meta-links {
|
||||
color: #a3a3a3;
|
||||
font-size: 15px;
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.foot-col {
|
||||
margin-bottom: 20px;
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.foot-header {
|
||||
font-weight: 500;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
margin-bottom: 2px;
|
||||
font-size: 15px;
|
||||
list-style: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
16
components/page-header/index.js
Normal file
16
components/page-header/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { HeaderWrap, Subtitle, Title } from './style';
|
||||
|
||||
const PageHeader = ({
|
||||
title,
|
||||
subtitle,
|
||||
children,
|
||||
}) => (
|
||||
<HeaderWrap>
|
||||
<Title>{ title }</Title>
|
||||
<Subtitle dangerouslySetInnerHTML={{ __html: subtitle }} />
|
||||
|
||||
{ children }
|
||||
</HeaderWrap>
|
||||
);
|
||||
|
||||
export default PageHeader;
|
||||
22
components/page-header/style.js
Normal file
22
components/page-header/style.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const HeaderWrap = styled.div`
|
||||
text-align: center;
|
||||
padding: 45px 30px;
|
||||
`;
|
||||
|
||||
export const Title = styled.h1`
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
`;
|
||||
|
||||
export const Subtitle = styled.p`
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
}
|
||||
`;
|
||||
13
components/page-logo-header/index.js
Normal file
13
components/page-logo-header/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { HeaderWrap, Subtitle, Title, Logo } from './style';
|
||||
|
||||
const PageLogoHeader = ({ title, subtitle, children, }) => (
|
||||
<HeaderWrap>
|
||||
<Logo src='/brand.png' alt='' />
|
||||
<Title>{ title }</Title>
|
||||
<Subtitle dangerouslySetInnerHTML={{ __html: subtitle }} />
|
||||
|
||||
{ children }
|
||||
</HeaderWrap>
|
||||
);
|
||||
|
||||
export default PageLogoHeader;
|
||||
30
components/page-logo-header/style.js
Normal file
30
components/page-logo-header/style.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const HeaderWrap = styled.div`
|
||||
text-align: center;
|
||||
padding: 45px 30px;
|
||||
`;
|
||||
|
||||
export const Title = styled.h1`
|
||||
font-size: 40px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
`;
|
||||
|
||||
export const Subtitle = styled.p`
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
margin-bottom: 0;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 700;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
export const Logo = styled.img`
|
||||
width: 75px;
|
||||
height: 75px;
|
||||
margin-bottom: 26px;
|
||||
`;
|
||||
62
components/roadmap-header/index.js
Normal file
62
components/roadmap-header/index.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faArrowLeft, faClock, faEnvelope, faHandshake } from '@fortawesome/free-solid-svg-icons';
|
||||
import { BadgeLink, BadgesList, DarkBadge, PrimaryBadge, SecondaryBadge } from 'components/badges';
|
||||
import siteConfig from "content/site";
|
||||
import { Description, Header, Title, MenuItemLink, MenuItems } from './style';
|
||||
import Link from 'next/link';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const RoadmapHeader = ({ roadmap, page = 'landscape' }) => (
|
||||
<Header>
|
||||
<Title>{ roadmap.title }</Title>
|
||||
<Description>{ roadmap.description }</Description>
|
||||
<BadgesList className="mt-4">
|
||||
<BadgeLink href="/roadmaps">
|
||||
<DarkBadge>
|
||||
<FontAwesomeIcon className='d-none d-md-block' icon={ faArrowLeft } />
|
||||
Other Roadmaps
|
||||
</DarkBadge>
|
||||
</BadgeLink>
|
||||
{ roadmap.upcoming && (
|
||||
<BadgeLink href="/signup">
|
||||
<SecondaryBadge>
|
||||
<FontAwesomeIcon className='d-none d-md-block' icon={ faClock } />
|
||||
Upcoming Roadmap
|
||||
</SecondaryBadge>
|
||||
</BadgeLink>
|
||||
) }
|
||||
{ !roadmap.upcoming && (
|
||||
<BadgeLink href={ `${siteConfig.url.issue}?title=[${roadmap.title}] - Title Here` } target="_blank" className='d-none d-md-block' >
|
||||
<SecondaryBadge>
|
||||
<FontAwesomeIcon icon={ faHandshake } />
|
||||
Suggest Changes
|
||||
</SecondaryBadge>
|
||||
</BadgeLink>
|
||||
) }
|
||||
|
||||
<BadgeLink href="/signup">
|
||||
<PrimaryBadge>
|
||||
<FontAwesomeIcon className='d-none d-md-block' icon={ faEnvelope } />
|
||||
Send me Updates
|
||||
</PrimaryBadge>
|
||||
</BadgeLink>
|
||||
</BadgesList>
|
||||
|
||||
<MenuItems className="border-bottom">
|
||||
<div className={ classNames({ 'd-none': roadmap.title.toLowerCase() !== 'frontend developer' })}>
|
||||
<Link href={ `${roadmap.url}` } passHref>
|
||||
<MenuItemLink className={ classNames({ active: page === 'landscape', }) }>Landscape</MenuItemLink>
|
||||
</Link>
|
||||
<Link href={ `${roadmap.url}/resources` } passHref>
|
||||
<MenuItemLink className={ classNames({ active: page === 'resources', }) }>Resources</MenuItemLink>
|
||||
</Link>
|
||||
{/*<Link href={ `${roadmap.url}/resources` } passHref>*/}
|
||||
{/* <MenuItemLink className={ classNames({ active: false, }) }>Project Ideas</MenuItemLink>*/}
|
||||
{/*</Link>*/}
|
||||
</div>
|
||||
</MenuItems>
|
||||
|
||||
</Header>
|
||||
);
|
||||
|
||||
export default RoadmapHeader;
|
||||
50
components/roadmap-header/style.js
Normal file
50
components/roadmap-header/style.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Header = styled.div`
|
||||
text-align: center;
|
||||
padding: 45px 30px 10px;
|
||||
`;
|
||||
|
||||
export const Title = styled.h1`
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
font-size: 48px;
|
||||
`;
|
||||
|
||||
export const Description = styled.p`
|
||||
font-size: 16px;
|
||||
color: #444444;
|
||||
`;
|
||||
|
||||
export const MenuItems = styled.div`
|
||||
margin: 35px 0 15px;
|
||||
`;
|
||||
|
||||
export const MenuItemLink = styled.a`
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 5px 10px 8px;
|
||||
text-decoration: none;
|
||||
color: rgb(102, 102, 102);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
&.active, &.active:hover {
|
||||
color: #2d2d2d;
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 0;
|
||||
left: 9px;
|
||||
right: 9px;
|
||||
bottom: -1px;
|
||||
border-bottom: 2px solid currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: #111111;
|
||||
}
|
||||
`;
|
||||
35
components/roadmap-resources/index.js
Normal file
35
components/roadmap-resources/index.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Summary, SummaryContainer, UpcomingContainer } from './style';
|
||||
import GuideBody from 'components/guide-body';
|
||||
import RoadmapHeader from 'components/roadmap-header';
|
||||
import SharePage from 'components/share-page';
|
||||
import MdRenderer from 'components/md-renderer';
|
||||
|
||||
const RoadmapResources = ({ roadmap }) => {
|
||||
if (roadmap.upcoming) {
|
||||
return (
|
||||
<>
|
||||
<RoadmapHeader roadmap={ roadmap } />
|
||||
<UpcomingContainer>
|
||||
<GuideBody guide={{ fileName: 'upcoming' }} />
|
||||
</UpcomingContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const filePath = roadmap.resources.replace(/^\//, '');
|
||||
const ResourcesContent = require(`../../content/${filePath}`).default;
|
||||
|
||||
return (
|
||||
<SummaryContainer>
|
||||
<RoadmapHeader roadmap={ roadmap } page='resources' />
|
||||
<Summary className="container">
|
||||
<MdRenderer>
|
||||
<ResourcesContent />
|
||||
</MdRenderer>
|
||||
<SharePage title={ roadmap.description } url={ roadmap.url } />
|
||||
</Summary>
|
||||
</SummaryContainer>
|
||||
)
|
||||
};
|
||||
|
||||
export default RoadmapResources;
|
||||
21
components/roadmap-resources/style.js
Normal file
21
components/roadmap-resources/style.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SummaryContainer = styled.div``;
|
||||
|
||||
export const UpcomingContainer = styled.div`
|
||||
text-align: center;
|
||||
padding: 40px 0 50px;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Summary = styled.div`
|
||||
margin-top: 35px;
|
||||
min-height: 400px;
|
||||
max-width: 1000px;
|
||||
display: block;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
`;
|
||||
36
components/roadmap-summary/index.js
Normal file
36
components/roadmap-summary/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Summary, SummaryContainer, UpcomingContainer } from './style';
|
||||
import classNames from 'classnames';
|
||||
import GuideBody from 'components/guide-body';
|
||||
import RoadmapHeader from 'components/roadmap-header';
|
||||
import SharePage from 'components/share-page';
|
||||
import MdRenderer from 'components/md-renderer';
|
||||
|
||||
const RoadmapSummary = ({ roadmap }) => {
|
||||
if (roadmap.upcoming) {
|
||||
return (
|
||||
<>
|
||||
<RoadmapHeader roadmap={ roadmap } />
|
||||
<UpcomingContainer>
|
||||
<GuideBody guide={{ fileName: 'upcoming' }} />
|
||||
</UpcomingContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const filePath = roadmap.path.replace(/^\//, '');
|
||||
const RoadmapContent = require(`../../content/${filePath}`).default;
|
||||
|
||||
return (
|
||||
<SummaryContainer>
|
||||
<RoadmapHeader roadmap={ roadmap } />
|
||||
<Summary className={classNames("container", { "container-small": roadmap.isTextHeavy })}>
|
||||
<MdRenderer>
|
||||
<RoadmapContent />
|
||||
</MdRenderer>
|
||||
<SharePage title={ roadmap.description } url={ roadmap.url } />
|
||||
</Summary>
|
||||
</SummaryContainer>
|
||||
)
|
||||
};
|
||||
|
||||
export default RoadmapSummary;
|
||||
21
components/roadmap-summary/style.js
Normal file
21
components/roadmap-summary/style.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SummaryContainer = styled.div``;
|
||||
|
||||
export const UpcomingContainer = styled.div`
|
||||
text-align: center;
|
||||
padding: 40px 0 50px;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Summary = styled.div`
|
||||
margin-top: 35px;
|
||||
min-height: 400px;
|
||||
max-width: 1000px;
|
||||
display: block;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
`;
|
||||
28
components/row-block/index.js
Normal file
28
components/row-block/index.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Badge, BlockItem, ItemSubtitle, ItemTitle, ItemWrap } from './style';
|
||||
|
||||
const RowBlock = ({
|
||||
title,
|
||||
subtitle,
|
||||
url,
|
||||
badge,
|
||||
openExternal = false,
|
||||
disabled = false,
|
||||
children = null
|
||||
}) => (
|
||||
<ItemWrap className="col-md-6 col-lg-4 col-xl-4">
|
||||
<BlockItem href={ url } disabled={ disabled } target={openExternal ? '_blank' : '_self'}>
|
||||
{ !children && (
|
||||
<>
|
||||
<ItemTitle>
|
||||
{ title }
|
||||
{ badge && <Badge>{ badge }</Badge>}
|
||||
</ItemTitle>
|
||||
<ItemSubtitle>{ subtitle }</ItemSubtitle>
|
||||
</>
|
||||
) }
|
||||
{ children }
|
||||
</BlockItem>
|
||||
</ItemWrap>
|
||||
);
|
||||
|
||||
export default RowBlock;
|
||||
62
components/row-block/style.js
Normal file
62
components/row-block/style.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const ItemWrap = styled.div`
|
||||
padding: 0 10px 20px;
|
||||
`;
|
||||
|
||||
export const BlockItem = styled.a`
|
||||
min-height: 114px;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 5px 10px;
|
||||
transition: box-shadow 0.2s ease 0s;
|
||||
align-items: stretch;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 1px;
|
||||
max-width: 100%;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
justify-content: center;
|
||||
text-decoration:none;
|
||||
opacity: ${ props => props.disabled ? 0.5 : 1 };
|
||||
|
||||
&:hover {
|
||||
text-decoration:none;
|
||||
box-shadow: rgba(0, 0, 0, 0.12) 0 30px 60px;
|
||||
z-index: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ItemTitle = styled.h3`
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
color: #101010;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
export const Badge = styled.span`
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
background: #8a8a8a;
|
||||
color: white;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
text-transform: uppercase;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 10px;
|
||||
height: 18px;
|
||||
`;
|
||||
|
||||
export const ItemSubtitle = styled.p`
|
||||
font-size: 14px;
|
||||
color: rgb(102, 102, 102);
|
||||
white-space: normal;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
`;
|
||||
21
components/share-icon/index.js
Normal file
21
components/share-icon/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const ShareIcon = styled.a`
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
|
||||
svg {
|
||||
height: 22px !important;
|
||||
width: 22px !important;
|
||||
color: #757575;
|
||||
transition: all 0.2s;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
color: #000000
|
||||
}
|
||||
`;
|
||||
37
components/share-page/index.js
Normal file
37
components/share-page/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faFacebookSquare, faHackerNewsSquare, faRedditSquare, faTwitterSquare } from '@fortawesome/free-brands-svg-icons';
|
||||
|
||||
import { getFacebookShareUrl, getHnShareUrl, getRedditShareUrl, getTwitterShareUrl } from 'lib/url';
|
||||
import { ShareIconsList, ShareWrap } from './style';
|
||||
import { ShareIcon } from 'components/share-icon';
|
||||
|
||||
const SharePage = ({
|
||||
url,
|
||||
title,
|
||||
twitterUsername,
|
||||
}) => (
|
||||
<ShareWrap>
|
||||
<ShareIconsList className="d-none d-sm-flex">
|
||||
<ShareIcon
|
||||
href={ getTwitterShareUrl({
|
||||
text: `${title} ${twitterUsername ? `by @${twitterUsername}` : ''}`,
|
||||
url: url
|
||||
})}
|
||||
target="_blank"
|
||||
>
|
||||
<FontAwesomeIcon icon={ faTwitterSquare } />
|
||||
</ShareIcon>
|
||||
<ShareIcon href={ getFacebookShareUrl({ text: title, url: url }) } target="_blank">
|
||||
<FontAwesomeIcon icon={ faFacebookSquare } />
|
||||
</ShareIcon>
|
||||
<ShareIcon href={ getHnShareUrl({ text: title, url: url })} target="_blank">
|
||||
<FontAwesomeIcon icon={faHackerNewsSquare}/>
|
||||
</ShareIcon>
|
||||
<ShareIcon href={ getRedditShareUrl({ text: title, url: url })} target="_blank">
|
||||
<FontAwesomeIcon icon={ faRedditSquare } />
|
||||
</ShareIcon>
|
||||
</ShareIconsList>
|
||||
</ShareWrap>
|
||||
);
|
||||
|
||||
export default SharePage;
|
||||
21
components/share-page/style.js
Normal file
21
components/share-page/style.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const ShareIconsList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: sticky;
|
||||
top: 65px;
|
||||
`;
|
||||
|
||||
export const ShareWrap = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
padding: 0 0;
|
||||
top: 0;
|
||||
left: -40px;
|
||||
min-height: 100%;
|
||||
`;
|
||||
17
components/signup-form/index.js
Normal file
17
components/signup-form/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { SignUpWrap, Title, Subtitle, Textbox, Button } from './style';
|
||||
|
||||
const SignUpForm = () => (
|
||||
<SignUpWrap>
|
||||
<Title>Subscribe</Title>
|
||||
<Subtitle>Enter your email below to get notified about the new roadmaps, guides and updates</Subtitle>
|
||||
<form action="https://kamranahmed.us9.list-manage.com/subscribe/post?u=6f57b741a6a939744f1f203a0&id=f9ca4d6aee" target="_blank" method="post">
|
||||
<Textbox type="email" name="EMAIL" required placeholder="Your email" />
|
||||
<div style={{position: 'absolute', left: '-5000px'}}>
|
||||
<input type="text" name="b_6f57b741a6a939744f1f203a0_f9ca4d6aee" tabIndex="-1" />
|
||||
</div>
|
||||
<Button type="submit">Subscribe</Button>
|
||||
</form>
|
||||
</SignUpWrap>
|
||||
);
|
||||
|
||||
export default SignUpForm;
|
||||
57
components/signup-form/style.js
Normal file
57
components/signup-form/style.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SignUpWrap = styled.div`
|
||||
max-width: 400px;
|
||||
margin: 200px auto;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
export const Title = styled.h1`
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 17px;
|
||||
`;
|
||||
|
||||
export const Subtitle = styled.p`
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
export const Textbox = styled.input`
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
height: auto;
|
||||
outline: none;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 5px;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid #101010;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Button = styled.button`
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
padding: 10px 15px;
|
||||
margin-top: 10px;
|
||||
background: #333;
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
|
||||
&:hover, &:active, &:focus {
|
||||
background: #000000;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
26
components/site-nav/index.js
Normal file
26
components/site-nav/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { HeaderWrap } from './style';
|
||||
|
||||
const SiteNav = () => (
|
||||
<HeaderWrap>
|
||||
<div className='top-row container'>
|
||||
<div className='flex-grow-1 brand'>
|
||||
<a href='/'>
|
||||
<img src='/brand.png' alt='' />
|
||||
</a>
|
||||
</div>
|
||||
<div className='nav-links'>
|
||||
<a href='/guides'>Read</a>
|
||||
<a href='/watch'>
|
||||
Watch
|
||||
<span className='new-item' />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className='ml-3 align-items-center d-none d-md-flex'>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=kamranahmedse&repo=developer-roadmap&type=star&count=true&size=large" frameBorder="0" scrolling="0" width="190px" height="30px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</HeaderWrap>
|
||||
);
|
||||
|
||||
export default SiteNav;
|
||||
61
components/site-nav/style.js
Normal file
61
components/site-nav/style.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const HeaderWrap = styled.div`
|
||||
padding: 15px 0;
|
||||
font-size: 18px;
|
||||
|
||||
.top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> span {
|
||||
display: flex;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.github-button {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.brand img {
|
||||
padding: 5px 0;
|
||||
height: 50px;
|
||||
text-decoration: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
a {
|
||||
position: relative;
|
||||
padding: 0 10px;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.signup {
|
||||
background: #101010;
|
||||
border-radius: 5px;
|
||||
color: #ffffff;
|
||||
padding: 7px 10px;
|
||||
margin-left: 15px;
|
||||
|
||||
&:hover {
|
||||
background: #2d2d2d;
|
||||
}
|
||||
}
|
||||
|
||||
.new-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: inline-block;
|
||||
padding: 3px;
|
||||
background: #e25712;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
26
components/sponsor-banner/index.js
Normal file
26
components/sponsor-banner/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import { BannerWrap, CloseSponsor, SponsorLogo } from './style';
|
||||
|
||||
export function SponsorBanner({ onCloseBanner = () => null }) {
|
||||
return (
|
||||
<div className='row'>
|
||||
<div className='col p-0'>
|
||||
<BannerWrap
|
||||
href={`https://www.youtube.com/channel/UCA0H2KIWgWTwpTFjSxp0now?sub_confirmation=1`}
|
||||
target='_blank'
|
||||
className='alert alert-info'
|
||||
>
|
||||
<SponsorLogo src='/sponsors/youtube.svg' />
|
||||
We now have a youtube channel. <span className='d-none d-sm-inline-block'>Subscribe for the video content.</span>
|
||||
|
||||
<CloseSponsor
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onCloseBanner();
|
||||
}}
|
||||
className='close'>×</CloseSponsor>
|
||||
</BannerWrap>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user