mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2026-03-14 02:32:00 +08:00
Compare commits
369 Commits
fix/sync-r
...
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": 1758909687006
|
||||
}
|
||||
}
|
||||
1
.astro/types.d.ts
vendored
1
.astro/types.d.ts
vendored
@@ -1 +0,0 @@
|
||||
/// <reference types="astro/client" />
|
||||
13
.babelrc
Normal file
13
.babelrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"presets": [
|
||||
"next/babel"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"styled-components",
|
||||
{
|
||||
"ssr": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
---
|
||||
description: When user requests migrating old roadmap content to new folder from content-old to content folder
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Content Migration Rule
|
||||
|
||||
## Rule Name: content-migration
|
||||
|
||||
## Description
|
||||
This rule provides a complete process for migrating roadmap content from old structure to new structure using migration mapping files.
|
||||
|
||||
## When to Use
|
||||
Use this rule when you need to:
|
||||
- Migrate content from content-old directories to content directories
|
||||
- Use a migration-mapping.json file to map topic paths to content IDs
|
||||
- Populate empty content files with existing content from legacy structure
|
||||
|
||||
## Process
|
||||
|
||||
### 1. Prerequisites Check
|
||||
- Verify the roadmap directory has a `migration-mapping.json` file
|
||||
- Confirm `content-old/` directory exists with source content
|
||||
- Confirm `content/` directory exists with target files
|
||||
|
||||
### 2. Migration Script Creation
|
||||
Create a Node.js script with the following functionality:
|
||||
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Load the migration mapping
|
||||
const migrationMapping = JSON.parse(fs.readFileSync('migration-mapping.json', 'utf8'));
|
||||
|
||||
// Function to find old content file based on topic path
|
||||
function findOldContentFile(topicPath) {
|
||||
const parts = topicPath.split(':');
|
||||
|
||||
if (parts.length === 1) {
|
||||
// Top level file like "introduction"
|
||||
return path.join('content-old', parts[0], 'index.md');
|
||||
} else if (parts.length === 2) {
|
||||
// Like "introduction:what-is-rust"
|
||||
const [folder, filename] = parts;
|
||||
return path.join('content-old', folder, `${filename}.md`);
|
||||
} else if (parts.length === 3) {
|
||||
// Like "language-basics:syntax:variables"
|
||||
const [folder, subfolder, filename] = parts;
|
||||
return path.join('content-old', folder, subfolder, `${filename}.md`);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Function to find new content file based on content ID
|
||||
function findNewContentFile(contentId) {
|
||||
const contentDir = 'content';
|
||||
const files = fs.readdirSync(contentDir);
|
||||
|
||||
// Find file that ends with the content ID
|
||||
const matchingFile = files.find(file => file.includes(`@${contentId}.md`));
|
||||
|
||||
if (matchingFile) {
|
||||
return path.join(contentDir, matchingFile);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Process each mapping
|
||||
console.log('Starting content migration...\n');
|
||||
|
||||
let migratedCount = 0;
|
||||
let skippedCount = 0;
|
||||
|
||||
for (const [topicPath, contentId] of Object.entries(migrationMapping)) {
|
||||
const oldFilePath = findOldContentFile(topicPath);
|
||||
const newFilePath = findNewContentFile(contentId);
|
||||
|
||||
if (!oldFilePath) {
|
||||
console.log(`❌ Could not determine old file path for: ${topicPath}`);
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!newFilePath) {
|
||||
console.log(`❌ Could not find new file for content ID: ${contentId} (topic: ${topicPath})`);
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fs.existsSync(oldFilePath)) {
|
||||
console.log(`❌ Old file does not exist: ${oldFilePath} (topic: ${topicPath})`);
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Read old content
|
||||
const oldContent = fs.readFileSync(oldFilePath, 'utf8');
|
||||
|
||||
// Write to new file
|
||||
fs.writeFileSync(newFilePath, oldContent);
|
||||
|
||||
console.log(`✅ Migrated: ${topicPath} -> ${path.basename(newFilePath)}`);
|
||||
migratedCount++;
|
||||
} catch (error) {
|
||||
console.log(`❌ Error migrating ${topicPath}: ${error.message}`);
|
||||
skippedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n📊 Migration complete:`);
|
||||
console.log(` Migrated: ${migratedCount} files`);
|
||||
console.log(` Skipped: ${skippedCount} files`);
|
||||
console.log(` Total: ${Object.keys(migrationMapping).length} mappings`);
|
||||
```
|
||||
|
||||
### 3. Execution Steps
|
||||
1. Navigate to the roadmap directory (e.g., `src/data/roadmaps/[roadmap-name]`)
|
||||
2. Create the migration script as `migrate_content.cjs`
|
||||
3. Run: `node migrate_content.cjs`
|
||||
4. Review the migration results
|
||||
5. Clean up the temporary script file
|
||||
|
||||
### 4. Validation
|
||||
After migration:
|
||||
- Verify a few migrated files have proper content (not just titles)
|
||||
- Check that the content structure matches the old content
|
||||
- Ensure proper markdown formatting is preserved
|
||||
|
||||
## File Structure Expected
|
||||
```
|
||||
roadmap-directory/
|
||||
├── migration-mapping.json
|
||||
├── content/
|
||||
│ ├── file1@contentId1.md
|
||||
│ ├── file2@contentId2.md
|
||||
│ └── ...
|
||||
└── content-old/
|
||||
├── section1/
|
||||
│ ├── index.md
|
||||
│ ├── topic1.md
|
||||
│ └── subsection1/
|
||||
│ └── subtopic1.md
|
||||
└── section2/
|
||||
└── ...
|
||||
```
|
||||
|
||||
## Notes
|
||||
- The migration mapping uses colons (`:`) to separate nested paths
|
||||
- Content files in the new structure use the pattern `filename@contentId.md`
|
||||
- The script handles 1-3 levels of nesting in the old structure
|
||||
- Always create the script with `.cjs` extension to avoid ES module issues
|
||||
@@ -1,389 +0,0 @@
|
||||
---
|
||||
description: GitHub pull requests
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# gh cli
|
||||
|
||||
Work seamlessly with GitHub from the command line.
|
||||
|
||||
USAGE
|
||||
gh <command> <subcommand> [flags]
|
||||
|
||||
CORE COMMANDS
|
||||
auth: Authenticate gh and git with GitHub
|
||||
browse: Open repositories, issues, pull requests, and more in the browser
|
||||
codespace: Connect to and manage codespaces
|
||||
gist: Manage gists
|
||||
issue: Manage issues
|
||||
org: Manage organizations
|
||||
pr: Manage pull requests
|
||||
project: Work with GitHub Projects.
|
||||
release: Manage releases
|
||||
repo: Manage repositories
|
||||
|
||||
GITHUB ACTIONS COMMANDS
|
||||
cache: Manage GitHub Actions caches
|
||||
run: View details about workflow runs
|
||||
workflow: View details about GitHub Actions workflows
|
||||
|
||||
ALIAS COMMANDS
|
||||
co: Alias for "pr checkout"
|
||||
|
||||
ADDITIONAL COMMANDS
|
||||
alias: Create command shortcuts
|
||||
api: Make an authenticated GitHub API request
|
||||
attestation: Work with artifact attestations
|
||||
completion: Generate shell completion scripts
|
||||
config: Manage configuration for gh
|
||||
extension: Manage gh extensions
|
||||
gpg-key: Manage GPG keys
|
||||
label: Manage labels
|
||||
preview: Execute previews for gh features
|
||||
ruleset: View info about repo rulesets
|
||||
search: Search for repositories, issues, and pull requests
|
||||
secret: Manage GitHub secrets
|
||||
ssh-key: Manage SSH keys
|
||||
status: Print information about relevant issues, pull requests, and notifications across repositories
|
||||
variable: Manage GitHub Actions variables
|
||||
|
||||
HELP TOPICS
|
||||
accessibility: Learn about GitHub CLI's accessibility experiences
|
||||
actions: Learn about working with GitHub Actions
|
||||
environment: Environment variables that can be used with gh
|
||||
exit-codes: Exit codes used by gh
|
||||
formatting: Formatting options for JSON data exported from gh
|
||||
mintty: Information about using gh with MinTTY
|
||||
reference: A comprehensive reference of all gh commands
|
||||
|
||||
FLAGS
|
||||
--help Show help for command
|
||||
--version Show gh version
|
||||
|
||||
EXAMPLES
|
||||
$ gh issue create
|
||||
$ gh repo clone cli/cli
|
||||
$ gh pr checkout 321
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr
|
||||
|
||||
Work with GitHub pull requests.
|
||||
|
||||
USAGE
|
||||
gh pr <command> [flags]
|
||||
|
||||
GENERAL COMMANDS
|
||||
create: Create a pull request
|
||||
list: List pull requests in a repository
|
||||
status: Show status of relevant pull requests
|
||||
|
||||
TARGETED COMMANDS
|
||||
checkout: Check out a pull request in git
|
||||
checks: Show CI status for a single pull request
|
||||
close: Close a pull request
|
||||
comment: Add a comment to a pull request
|
||||
diff: View changes in a pull request
|
||||
edit: Edit a pull request
|
||||
lock: Lock pull request conversation
|
||||
merge: Merge a pull request
|
||||
ready: Mark a pull request as ready for review
|
||||
reopen: Reopen a pull request
|
||||
review: Add a review to a pull request
|
||||
unlock: Unlock pull request conversation
|
||||
update-branch: Update a pull request branch
|
||||
view: View a pull request
|
||||
|
||||
FLAGS
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
|
||||
ARGUMENTS
|
||||
A pull request can be supplied as argument in any of the following formats:
|
||||
- by number, e.g. "123";
|
||||
- by URL, e.g. "https://github.com/OWNER/REPO/pull/123"; or
|
||||
- by the name of its head branch, e.g. "patch-1" or "OWNER:patch-1".
|
||||
|
||||
EXAMPLES
|
||||
$ gh pr checkout 353
|
||||
$ gh pr create --fill
|
||||
$ gh pr view --web
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr list
|
||||
|
||||
List pull requests in a GitHub repository. By default, this only lists open PRs.
|
||||
|
||||
The search query syntax is documented here:
|
||||
<https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests>
|
||||
|
||||
For more information about output formatting flags, see `gh help formatting`.
|
||||
|
||||
USAGE
|
||||
gh pr list [flags]
|
||||
|
||||
ALIASES
|
||||
gh pr ls
|
||||
|
||||
FLAGS
|
||||
--app string Filter by GitHub App author
|
||||
-a, --assignee string Filter by assignee
|
||||
-A, --author string Filter by author
|
||||
-B, --base string Filter by base branch
|
||||
-d, --draft Filter by draft state
|
||||
-H, --head string Filter by head branch ("<owner>:<branch>" syntax not supported)
|
||||
-q, --jq expression Filter JSON output using a jq expression
|
||||
--json fields Output JSON with the specified fields
|
||||
-l, --label strings Filter by label
|
||||
-L, --limit int Maximum number of items to fetch (default 30)
|
||||
-S, --search query Search pull requests with query
|
||||
-s, --state string Filter by state: {open|closed|merged|all} (default "open")
|
||||
-t, --template string Format JSON output using a Go template; see "gh help formatting"
|
||||
-w, --web List pull requests in the web browser
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
JSON FIELDS
|
||||
additions, assignees, author, autoMergeRequest, baseRefName, baseRefOid, body,
|
||||
changedFiles, closed, closedAt, closingIssuesReferences, comments, commits,
|
||||
createdAt, deletions, files, fullDatabaseId, headRefName, headRefOid,
|
||||
headRepository, headRepositoryOwner, id, isCrossRepository, isDraft, labels,
|
||||
latestReviews, maintainerCanModify, mergeCommit, mergeStateStatus, mergeable,
|
||||
mergedAt, mergedBy, milestone, number, potentialMergeCommit, projectCards,
|
||||
projectItems, reactionGroups, reviewDecision, reviewRequests, reviews, state,
|
||||
statusCheckRollup, title, updatedAt, url
|
||||
|
||||
EXAMPLES
|
||||
# List PRs authored by you
|
||||
$ gh pr list --author "@me"
|
||||
|
||||
# List PRs with a specific head branch name
|
||||
$ gh pr list --head "typo"
|
||||
|
||||
# List only PRs with all of the given labels
|
||||
$ gh pr list --label bug --label "priority 1"
|
||||
|
||||
# Filter PRs using search syntax
|
||||
$ gh pr list --search "status:success review:required"
|
||||
|
||||
# Find a PR that introduced a given commit
|
||||
$ gh pr list --search "<SHA>" --state merged
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr diff
|
||||
|
||||
View changes in a pull request.
|
||||
|
||||
Without an argument, the pull request that belongs to the current branch
|
||||
is selected.
|
||||
|
||||
With `--web` flag, open the pull request diff in a web browser instead.
|
||||
|
||||
|
||||
USAGE
|
||||
gh pr diff [<number> | <url> | <branch>] [flags]
|
||||
|
||||
FLAGS
|
||||
--color string Use color in diff output: {always|never|auto} (default "auto")
|
||||
--name-only Display only names of changed files
|
||||
--patch Display diff in patch format
|
||||
-w, --web Open the pull request diff in the browser
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr merge
|
||||
|
||||
Merge a pull request on GitHub.
|
||||
|
||||
Without an argument, the pull request that belongs to the current branch
|
||||
is selected.
|
||||
|
||||
When targeting a branch that requires a merge queue, no merge strategy is required.
|
||||
If required checks have not yet passed, auto-merge will be enabled.
|
||||
If required checks have passed, the pull request will be added to the merge queue.
|
||||
To bypass a merge queue and merge directly, pass the `--admin` flag.
|
||||
|
||||
|
||||
USAGE
|
||||
gh pr merge [<number> | <url> | <branch>] [flags]
|
||||
|
||||
FLAGS
|
||||
--admin Use administrator privileges to merge a pull request that does not meet requirements
|
||||
-A, --author-email text Email text for merge commit author
|
||||
--auto Automatically merge only after necessary requirements are met
|
||||
-b, --body text Body text for the merge commit
|
||||
-F, --body-file file Read body text from file (use "-" to read from standard input)
|
||||
-d, --delete-branch Delete the local and remote branch after merge
|
||||
--disable-auto Disable auto-merge for this pull request
|
||||
--match-head-commit SHA Commit SHA that the pull request head must match to allow merge
|
||||
-m, --merge Merge the commits with the base branch
|
||||
-r, --rebase Rebase the commits onto the base branch
|
||||
-s, --squash Squash the commits into one commit and merge it into the base branch
|
||||
-t, --subject text Subject text for the merge commit
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr review
|
||||
|
||||
Add a review to a pull request.
|
||||
|
||||
Without an argument, the pull request that belongs to the current branch is reviewed.
|
||||
|
||||
|
||||
USAGE
|
||||
gh pr review [<number> | <url> | <branch>] [flags]
|
||||
|
||||
FLAGS
|
||||
-a, --approve Approve pull request
|
||||
-b, --body string Specify the body of a review
|
||||
-F, --body-file file Read body text from file (use "-" to read from standard input)
|
||||
-c, --comment Comment on a pull request
|
||||
-r, --request-changes Request changes on a pull request
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
EXAMPLES
|
||||
# Approve the pull request of the current branch
|
||||
$ gh pr review --approve
|
||||
|
||||
# Leave a review comment for the current branch
|
||||
$ gh pr review --comment -b "interesting"
|
||||
|
||||
# Add a review for a specific pull request
|
||||
$ gh pr review 123
|
||||
|
||||
# Request changes on a specific pull request
|
||||
$ gh pr review 123 -r -b "needs more ASCII art"
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr checkout
|
||||
|
||||
Check out a pull request in git
|
||||
|
||||
USAGE
|
||||
gh pr checkout [<number> | <url> | <branch>] [flags]
|
||||
|
||||
FLAGS
|
||||
-b, --branch string Local branch name to use (default [the name of the head branch])
|
||||
--detach Checkout PR with a detached HEAD
|
||||
-f, --force Reset the existing local branch to the latest state of the pull request
|
||||
--recurse-submodules Update all submodules after checkout
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
EXAMPLES
|
||||
# Interactively select a PR from the 10 most recent to check out
|
||||
$ gh pr checkout
|
||||
|
||||
# Checkout a specific PR
|
||||
$ gh pr checkout 32
|
||||
$ gh pr checkout https://github.com/OWNER/REPO/pull/32
|
||||
$ gh pr checkout feature
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr close
|
||||
|
||||
Close a pull request
|
||||
|
||||
USAGE
|
||||
gh pr close {<number> | <url> | <branch>} [flags]
|
||||
|
||||
FLAGS
|
||||
-c, --comment string Leave a closing comment
|
||||
-d, --delete-branch Delete the local and remote branch after close
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
## gh pr comment
|
||||
|
||||
Add a comment to a GitHub pull request.
|
||||
|
||||
Without the body text supplied through flags, the command will interactively
|
||||
prompt for the comment text.
|
||||
|
||||
|
||||
USAGE
|
||||
gh pr comment [<number> | <url> | <branch>] [flags]
|
||||
|
||||
FLAGS
|
||||
-b, --body text The comment body text
|
||||
-F, --body-file file Read body text from file (use "-" to read from standard input)
|
||||
--create-if-none Create a new comment if no comments are found. Can be used only with --edit-last
|
||||
--delete-last Delete the last comment of the current user
|
||||
--edit-last Edit the last comment of the current user
|
||||
-e, --editor Skip prompts and open the text editor to write the body in
|
||||
-w, --web Open the web browser to write the comment
|
||||
--yes Skip the delete confirmation prompt when --delete-last is provided
|
||||
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
-R, --repo [HOST/]OWNER/REPO Select another repository using the [HOST/]OWNER/REPO format
|
||||
|
||||
EXAMPLES
|
||||
$ gh pr comment 13 --body "Hi from GitHub CLI"
|
||||
|
||||
LEARN MORE
|
||||
Use `gh <command> <subcommand> --help` for more information about a command.
|
||||
Read the manual at https://cli.github.com/manual
|
||||
Learn about exit codes using `gh help exit-codes`
|
||||
Learn about accessibility experiences using `gh help accessibility`
|
||||
|
||||
|
||||
|
||||
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
|
||||
12
.env.example
12
.env.example
@@ -1,12 +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
|
||||
PUBLIC_COURSE_APP_URL=http://localhost:5173
|
||||
|
||||
PUBLIC_STRIPE_INDIVIDUAL_MONTHLY_PRICE_ID=
|
||||
PUBLIC_STRIPE_INDIVIDUAL_YEARLY_PRICE_ID=
|
||||
|
||||
PUBLIC_STRIPE_INDIVIDUAL_MONTHLY_PRICE_AMOUNT=10
|
||||
PUBLIC_STRIPE_INDIVIDUAL_YEARLY_PRICE_AMOUNT=100
|
||||
|
||||
ROADMAP_API_KEY=
|
||||
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
|
||||
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-api-cache.yml
vendored
16
.github/workflows/cloudfront-api-cache.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Clears API Cloudfront Cache
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
cloudfront_api_cache:
|
||||
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-api", "is_verbose": false } }'
|
||||
16
.github/workflows/cloudfront-fe-cache.yml
vendored
16
.github/workflows/cloudfront-fe-cache.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Clears Frontend Cloudfront Cache
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
cloudfront_fe_cache:
|
||||
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,cloudfront-course", "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
|
||||
75
.github/workflows/deployment.yml
vendored
75
.github/workflows/deployment.yml
vendored
@@ -1,75 +0,0 @@
|
||||
name: Deploy to EC2
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- uses: pnpm/action-setup@v4.0.0
|
||||
with:
|
||||
version: 9
|
||||
|
||||
# -------------------
|
||||
# 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 Production 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 Application 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 } }'
|
||||
40
.github/workflows/label-issue.yml
vendored
40
.github/workflows/label-issue.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Label Issue
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [ opened, edited ]
|
||||
|
||||
jobs:
|
||||
label-topic-change-issue:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add Labels To Issue
|
||||
uses: actions/github-script@v7
|
||||
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.rest.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.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
name: Refresh Roadmap Content JSON
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
refresh-content:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm@v9
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js Version 20 (LTS)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
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@v7
|
||||
with:
|
||||
delete-branch: false
|
||||
branch: "chore/update-content-json"
|
||||
base: "master"
|
||||
labels: |
|
||||
dependencies
|
||||
automated pr
|
||||
reviewers: kamranahmedse
|
||||
commit-message: "chore: update roadmap content json"
|
||||
title: "Updated Roadmap Content JSON - Automated"
|
||||
body: |
|
||||
## Updated Roadmap Content JSON
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This PR Updates the Roadmap Content JSON files stored in the `public` directory.
|
||||
>
|
||||
> Commit: ${{ github.sha }}
|
||||
> Workflow Path: ${{ github.workflow_ref }}
|
||||
|
||||
**Please Review the Changes and Merge the PR if everything is fine.**
|
||||
66
.github/workflows/sync-content-to-repo.yml
vendored
66
.github/workflows/sync-content-to-repo.yml
vendored
@@ -1,66 +0,0 @@
|
||||
name: Sync Content to Repo
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
roadmap_slug:
|
||||
description: "The ID of the roadmap to sync"
|
||||
required: true
|
||||
default: "__default__"
|
||||
|
||||
jobs:
|
||||
sync-content:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm@v9
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js Version 20 (LTS)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install Dependencies and Sync Content
|
||||
run: |
|
||||
echo "Installing Dependencies"
|
||||
pnpm install
|
||||
echo "Syncing Content to Repo"
|
||||
npm run sync:content-to-repo -- --roadmap-slug=${{ inputs.roadmap_slug }} --secret=${{ secrets.GH_SYNC_SECRET }}
|
||||
|
||||
- name: Check for changes
|
||||
id: verify-changed-files
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "changed=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Create PR
|
||||
if: steps.verify-changed-files.outputs.changed == 'true'
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
delete-branch: false
|
||||
branch: "chore/sync-content-to-repo-${{ inputs.roadmap_slug }}"
|
||||
base: "master"
|
||||
labels: |
|
||||
automated pr
|
||||
reviewers: jcanalesluna,kamranahmedse
|
||||
commit-message: "chore: sync content to repo"
|
||||
title: "chore: sync content to repository - ${{ inputs.roadmap_slug }}"
|
||||
body: |
|
||||
## Sync Content to Repo
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This PR Syncs the Content to the Repo for the Roadmap: ${{ inputs.roadmap_slug }}
|
||||
>
|
||||
> Commit: ${{ github.sha }}
|
||||
> Workflow Path: ${{ github.workflow_ref }}
|
||||
|
||||
**Please Review the Changes and Merge the PR if everything is fine.**
|
||||
67
.github/workflows/sync-repo-to-database.yml
vendored
67
.github/workflows/sync-repo-to-database.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: Sync on Roadmap Changes
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'src/data/roadmaps/**'
|
||||
|
||||
jobs:
|
||||
sync-on-changes:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor != 'github-actions[bot]' && github.actor != 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2 # Fetch previous commit to compare changes
|
||||
|
||||
- name: Setup pnpm@v9
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js Version 20 (LTS)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
run: |
|
||||
echo "Getting changed files in /src/data/roadmaps/"
|
||||
|
||||
# Get changed files between HEAD and previous commit
|
||||
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD -- src/data/roadmaps/)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "No changes found in roadmaps directory"
|
||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Changed files:"
|
||||
echo "$CHANGED_FILES"
|
||||
|
||||
# Convert to space-separated list for the script
|
||||
CHANGED_FILES_LIST=$(echo "$CHANGED_FILES" | tr '\n' ',')
|
||||
|
||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||
echo "changed_files=$CHANGED_FILES_LIST" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install Dependencies
|
||||
if: steps.changed-files.outputs.has_changes == 'true'
|
||||
run: |
|
||||
echo "Installing Dependencies"
|
||||
pnpm install
|
||||
|
||||
- name: Run sync script with changed files
|
||||
if: steps.changed-files.outputs.has_changes == 'true'
|
||||
run: |
|
||||
echo "Running sync script for changed roadmap files"
|
||||
echo "Changed files: ${{ steps.changed-files.outputs.changed_files }}"
|
||||
|
||||
# Run your script with the changed file paths
|
||||
npm run sync:repo-to-database -- --files="${{ steps.changed-files.outputs.changed_files }}" --secret=${{ secrets.GH_SYNC_SECRET }}
|
||||
51
.github/workflows/upgrade-dependencies.yml
vendored
51
.github/workflows/upgrade-dependencies.yml
vendored
@@ -1,51 +0,0 @@
|
||||
name: Upgrade Dependencies
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0'
|
||||
|
||||
jobs:
|
||||
upgrade-deps:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js Version 20 (LTS)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Setup pnpm@v9
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- name: Install & Upgrade Dependencies
|
||||
run: |
|
||||
pnpm install
|
||||
npm run upgrade
|
||||
pnpm install --lockfile-only
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
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 - Automated"
|
||||
body: |
|
||||
## Updated all Dependencies to Latest Versions.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This PR Upgrades the Dependencies to the their latest versions.
|
||||
>
|
||||
> Commit: ${{ github.sha }}
|
||||
> Workflow Path: ${{ github.workflow_ref }}
|
||||
|
||||
**Please Review the Changes and Merge the PR if everything is fine.**
|
||||
45
.gitignore
vendored
45
.gitignore
vendored
@@ -1,33 +1,14 @@
|
||||
.idea
|
||||
.temp
|
||||
.astro
|
||||
|
||||
# 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
|
||||
*.csveditor/
|
||||
|
||||
packages/editor
|
||||
.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"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
.vscode/settings.json
vendored
14
.vscode/settings.json
vendored
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"prettier.documentSelectors": ["**/*.astro"],
|
||||
"[astro]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["\\b\\w+[cC]lassName\\s*=\\s*[\"']([^\"']*)[\"']"],
|
||||
["\\b\\w+[cC]lassName\\s*=\\s*`([^`]*)`"],
|
||||
["[\\w]+[cC]lassName[\"']?\\s*:\\s*[\"']([^\"']*)[\"']"],
|
||||
["[\\w]+[cC]lassName[\"']?\\s*:\\s*`([^`]*)`"],
|
||||
["cva\\(((?:[^()]|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
|
||||
["cx\\(((?:[^()]|\\([^()]*\\))*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
|
||||
]
|
||||
}
|
||||
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,76 +0,0 @@
|
||||
// https://astro.build/config
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import node from '@astrojs/node';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import rehypeExternalLinks from 'rehype-external-links';
|
||||
import { serializeSitemap, shouldIndexPage } from './sitemap.mjs';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
import react from '@astrojs/react';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://roadmap.sh/',
|
||||
redirects: {
|
||||
'/devops/devops-engineer': {
|
||||
status: 301,
|
||||
destination: '/devops',
|
||||
},
|
||||
'/ai-tutor': {
|
||||
status: 301,
|
||||
destination: '/ai',
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
server: {
|
||||
allowedHosts: ['roadmap.sh', 'port3k.kamranahmed.info'],
|
||||
},
|
||||
},
|
||||
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: 'server',
|
||||
adapter: node({
|
||||
mode: 'standalone',
|
||||
}),
|
||||
trailingSlash: 'never',
|
||||
integrations: [
|
||||
sitemap({
|
||||
filter: shouldIndexPage,
|
||||
serialize: serializeSitemap,
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
ssr: {
|
||||
noExternal: [/^@roadmapsh\/editor.*$/],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -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%;
|
||||
`;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user