├── .eslintrc.json
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── README.md
├── lint-staged.config.js
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── avatars
│ └── Avatar.png
├── favicon.ico
└── vercel.svg
├── src
├── assets
│ └── icons
│ │ ├── angular.svg
│ │ ├── arrow-up-right.svg
│ │ ├── css.svg
│ │ ├── discord.svg
│ │ ├── docker.svg
│ │ ├── facebook.svg
│ │ ├── figma.svg
│ │ ├── git.svg
│ │ ├── github-link.svg
│ │ ├── github.svg
│ │ ├── html.svg
│ │ ├── index.ts
│ │ ├── js.svg
│ │ ├── link.svg
│ │ ├── linkedin.svg
│ │ ├── node.svg
│ │ ├── npm.svg
│ │ ├── react.svg
│ │ ├── redux.svg
│ │ ├── remix.svg
│ │ ├── sass.svg
│ │ ├── svelte.svg
│ │ ├── ts.svg
│ │ ├── twitter.svg
│ │ ├── visual.svg
│ │ ├── vue.svg
│ │ └── webpack.svg
├── components
│ ├── AuthorCard.tsx
│ ├── Avatar.tsx
│ ├── Badge.tsx
│ ├── BlogCard.tsx
│ ├── Button.tsx
│ ├── Container.tsx
│ ├── Footer.tsx
│ ├── Header.tsx
│ ├── Hero.tsx
│ ├── IconButton.tsx
│ ├── Input.tsx
│ ├── Pagination
│ │ ├── Pagination.tsx
│ │ └── Pagination.types.ts
│ ├── Quote
│ │ └── QuoteSlider.tsx
│ ├── TeamCard.tsx
│ ├── index.ts
│ └── sections
│ │ ├── FaqSection.tsx
│ │ ├── FeaturesSection.tsx
│ │ ├── MetricsSection.tsx
│ │ ├── QuotesSection.tsx
│ │ └── TechnologiesSection.tsx
├── content
│ └── blog
│ │ └── oguz-blog-test.mdx
├── data
│ ├── faq.ts
│ ├── features.ts
│ ├── navigation.ts
│ ├── quotes.ts
│ └── technologies.ts
├── pages
│ ├── _app.tsx
│ ├── about.tsx
│ ├── api
│ │ └── hello.ts
│ ├── blog
│ │ ├── [slug].tsx
│ │ └── index.tsx
│ └── index.tsx
├── styles
│ └── globals.css
├── types
│ └── index.ts
└── utils
│ ├── blog.js
│ └── pagination.util.ts
├── tailwind.config.js
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "next/core-web-vitals",
4 | "prettier",
5 | "plugin:tailwindcss/recommended"
6 | ],
7 | "plugins": ["prettier", "tailwindcss", "simple-import-sort"],
8 | "rules": {
9 | "quotes": ["error", "single"],
10 | "prettier/prettier": "warn",
11 | "no-console": "warn",
12 | "simple-import-sort/imports": "error",
13 | "no-duplicate-imports": "error",
14 | "no-unused-vars": ["error", { "args": "all" }],
15 | "no-duplicate-case": "error",
16 | "no-empty": "error",
17 | "no-use-before-define": "error",
18 | "tailwindcss/classnames-order": "warn",
19 | "tailwindcss/enforces-negative-arbitrary-values": "warn",
20 | "tailwindcss/enforces-shorthand": "warn",
21 | "tailwindcss/migration-from-tailwind-2": "warn",
22 | "tailwindcss/no-arbitrary-value": "off",
23 | "tailwindcss/no-custom-classname": "warn",
24 | "tailwindcss/no-contradicting-classname": "error"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .idea
8 |
9 | # next.js
10 | /.next/
11 | /out/
12 |
13 | # production
14 | /build
15 |
16 | # misc
17 | .DS_Store
18 | *.pem
19 |
20 | # debug
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | .pnpm-debug.log*
25 |
26 | # local env files
27 | .env*.local
28 |
29 | # vercel
30 | .vercel
31 |
32 | # typescript
33 | *.tsbuildinfo
34 | next-env.d.ts
35 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | echo 'Running Git Hooks'
5 |
6 | echo "🔎 Checking validity of types with TypeScript"
7 |
8 | yarn type-check || (
9 | "\n⛔️ There is a type error in the code, fix it, and try commit again. ⛔️";
10 | false;
11 | )
12 |
13 |
14 | echo "\n✅ No TypeError found"
15 | echo "\n🔎 Running linter.."
16 |
17 | yarn lint || (
18 | echo '\n⛔️ There is a problem in the code. ⌛️ I run linter autofix for you.';
19 |
20 | echo '\n🔎 Running linter autofix..'
21 | yarn lint:fix || (
22 | echo '\n⛔️ Autofix failed. Please fix the linting errors manually. ⛔️';
23 | false;
24 | )
25 |
26 | echo '\n🧐 Please check the changes and commit again.\n'
27 | false;
28 | )
29 |
30 | echo '✅ No Eslint error found'
31 | echo '⌛️ Running lint staged and git commit ⌛️'
32 |
33 | npx lint-staged
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .next
2 | .cache
3 | package-lock.json
4 | public
5 | node_modules
6 | next-env.d.ts
7 | next.config.ts
8 | yarn.lock
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "none",
4 | "arrowParens": "avoid",
5 | "proseWrap": "preserve",
6 | "quoteProps": "as-needed",
7 | "bracketSameLine": false,
8 | "bracketSpacing": true,
9 | "tabWidth": 2
10 | }
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with
2 | [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
3 |
4 | ## Getting Started
5 |
6 | First, run the development server:
7 |
8 | ```bash
9 | npm run dev
10 | # or
11 | yarn dev
12 | ```
13 |
14 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the
15 | result.
16 |
17 | You can start editing the page by modifying `pages/Avatar.tsx`. The page
18 | auto-updates as you edit the file.
19 |
20 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on
21 | [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This
22 | endpoint can be edited in `pages/api/hello.ts`.
23 |
24 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are
25 | treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead
26 | of React pages.
27 |
28 | ## Learn More
29 |
30 | To learn more about Next.js, take a look at the following resources:
31 |
32 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js
33 | features and API.
34 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
35 |
36 | You can check out
37 | [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your
38 | feedback and contributions are welcome!
39 |
40 | ## Deploy on Vercel
41 |
42 | The easiest way to deploy your Next.js app is to use the
43 | [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme)
44 | from the creators of Next.js.
45 |
46 | Check out our
47 | [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more
48 | details.
49 |
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '**/*.(ts|tsx)': () => 'yarn tsc --noEmit',
3 |
4 | '**/*.(ts|tsx|js)': filenames => [
5 | `yarn eslint --fix ${filenames.join(' ')}`,
6 | `yarn prettier --write ${filenames.join(' ')}`
7 | ],
8 |
9 | '**/*.(md|json)': filenames => `yarn prettier --write ${filenames.join(' ')}`
10 | };
11 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 |
3 | const withSvgr = require('next-svgr');
4 |
5 | const nextConfig = {
6 | reactStrictMode: true,
7 | swcMinify: true,
8 | webpack(config) {
9 | return config;
10 | },
11 | images: {
12 | domains: ['i.ytimg.com', 'www.gravatar.com']
13 | },
14 | env: {
15 | DISCORD_SERVER_URL: 'https://discord.gg/frontendship'
16 | }
17 | };
18 |
19 | module.exports = withSvgr(nextConfig);
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontendship-website",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "lint:fix": "next lint --fix .",
11 | "format": "npx prettier --write .",
12 | "commit": "cz",
13 | "type-check": "tsc --noEmit"
14 | },
15 | "dependencies": {
16 | "@headlessui/react": "^1.7.13",
17 | "@next/font": "^13.0.5",
18 | "@tailwindcss/typography": "^0.5.9",
19 | "@types/node": "18.11.9",
20 | "@types/react": "18.0.25",
21 | "@types/react-dom": "18.0.9",
22 | "class-variance-authority": "^0.4.0",
23 | "commitizen": "^4.2.6",
24 | "cz-conventional-changelog": "^3.3.0",
25 | "eslint": "8.28.0",
26 | "eslint-config-next": "13.0.5",
27 | "eslint-config-prettier": "^8.5.0",
28 | "eslint-plugin-prettier": "^4.2.1",
29 | "gray-matter": "^4.0.3",
30 | "next": "13.0.5",
31 | "next-mdx-remote": "^4.2.0",
32 | "next-svgr": "^0.0.2",
33 | "path": "^0.12.7",
34 | "prettier": "^2.8.0",
35 | "react": "18.2.0",
36 | "react-dom": "18.2.0",
37 | "react-feather": "^2.0.10",
38 | "react-hook-form": "^7.42.0",
39 | "reading-time": "^1.5.0",
40 | "rehype-pretty-code": "^0.3.2",
41 | "rehype-slug": "^5.1.0",
42 | "shiki": "^0.11.1",
43 | "swiper": "^8.4.6",
44 | "tailwind-merge": "^1.8.1",
45 | "typescript": "4.9.3"
46 | },
47 | "devDependencies": {
48 | "@svgr/webpack": "^6.5.1",
49 | "autoprefixer": "^10.4.13",
50 | "eslint-plugin-simple-import-sort": "^8.0.0",
51 | "eslint-plugin-tailwindcss": "^3.8.0",
52 | "husky": "^8.0.3",
53 | "lint-staged": "^13.1.0",
54 | "postcss": "^8.4.19",
55 | "tailwindcss": "^3.2.4"
56 | },
57 | "config": {
58 | "commitizen": {
59 | "path": "./node_modules/cz-conventional-changelog"
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/public/avatars/Avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontendship/frontendship-website/966990c1d033a2624c1efce30cb5d67e4ffaafa5/public/avatars/Avatar.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontendship/frontendship-website/966990c1d033a2624c1efce30cb5d67e4ffaafa5/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/angular.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/arrow-up-right.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/css.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/discord.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/docker.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/assets/icons/facebook.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/figma.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/assets/icons/git.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/github-link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/html.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/index.ts:
--------------------------------------------------------------------------------
1 | export { default as AngularIcon } from './angular.svg';
2 | export { default as DockerIcon } from './docker.svg';
3 | export { default as FigmaIcon } from './figma.svg';
4 | export { default as GitIcon } from './git.svg';
5 | export { default as GithubIcon } from './github.svg';
6 | export { default as HTMLIcon } from './html.svg';
7 | export { default as JavaScriptIcon } from './js.svg';
8 | export { default as NodeIcon } from './node.svg';
9 | export { default as NpmIcon } from './npm.svg';
10 | export { default as ReactIcon } from './react.svg';
11 | export { default as ReduxIcon } from './redux.svg';
12 | export { default as VisualStudioIcon } from './visual.svg';
13 | export { default as VueIcon } from './vue.svg';
14 | export { default as WebpackIcon } from './webpack.svg';
15 | export { default as TypeScriptIcon } from './ts.svg';
16 | export { default as RemixRunIcon } from './remix.svg';
17 |
18 | export { default as TwitterIcon } from './twitter.svg';
19 | export { default as FacebookIcon } from './facebook.svg';
20 | export { default as LinkedinIcon } from './linkedin.svg';
21 | export { default as LinkIcon } from './link.svg';
22 | export { default as GithubLinkIcon } from './github-link.svg';
23 | export { default as DiscordIcon } from './discord.svg';
24 |
--------------------------------------------------------------------------------
/src/assets/icons/js.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/assets/icons/link.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/linkedin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/node.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/npm.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/react.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/redux.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/remix.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 | Shape
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/assets/icons/sass.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/assets/icons/svelte.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/ts.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/assets/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/icons/visual.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/assets/icons/vue.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/icons/webpack.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/AuthorCard.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export function AuthorCard({ children }: { children: React.ReactNode }) {
4 | return {children} ;
5 | }
6 |
7 | AuthorCard.Avatar = function AuthorAvatar({
8 | source
9 | }: {
10 | source: { src: string; alt: string };
11 | }) {
12 | return (
13 |
18 | );
19 | };
20 |
21 | AuthorCard.Info = function AuthorInfo({
22 | children
23 | }: {
24 | children: React.ReactNode;
25 | }) {
26 | return
{children}
;
27 | };
28 |
29 | AuthorCard.Name = function AuthorName({
30 | children
31 | }: {
32 | children: React.ReactNode;
33 | }) {
34 | return (
35 | {children}
36 | );
37 | };
38 |
39 | AuthorCard.Position = function AuthorPosition({
40 | children
41 | }: {
42 | children: string;
43 | }) {
44 | return {children} ;
45 | };
46 |
--------------------------------------------------------------------------------
/src/components/Avatar.tsx:
--------------------------------------------------------------------------------
1 | import Image, { ImageProps } from 'next/image';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export const SIZES = {
5 | xs: 24,
6 | sm: 32,
7 | md: 40,
8 | lg: 48,
9 | xl: 56,
10 | '2xl': 64
11 | };
12 |
13 | interface Props extends ImageProps {
14 | className?: string;
15 | size?: keyof typeof SIZES;
16 | src: string;
17 | alt: string;
18 | }
19 |
20 | export const Avatar = ({
21 | className,
22 | size = 'md',
23 | src,
24 | alt,
25 | ...props
26 | }: Props) => (
27 |
38 | );
39 |
40 | export default Avatar;
41 |
--------------------------------------------------------------------------------
/src/components/Badge.tsx:
--------------------------------------------------------------------------------
1 | import { cva, VariantProps } from 'class-variance-authority';
2 | import { ReactNode } from 'react';
3 |
4 | const badgeStyles = cva(
5 | [
6 | 'font-medium',
7 | 'rounded-3xl',
8 | 'flex',
9 | 'shrink-0',
10 | 'items-center',
11 | 'justify-center',
12 | 'w-fit',
13 | 'h-fit'
14 | ],
15 | {
16 | variants: {
17 | variant: {
18 | primary: [
19 | 'bg-violet-50',
20 | 'text-violet-700',
21 | '[&>.badge-title]:text-violet-50',
22 | '[&>.badge-title]:bg-violet-700'
23 | ],
24 | light: ['bg-violet-50', 'text-violet-700', '[&>.badge-title]:bg-white'],
25 | green: [
26 | 'bg-green-50',
27 | 'text-green-700',
28 | '[&>.badge-title]:bg-green-700',
29 | '[&>.badge-title]:text-white'
30 | ],
31 | blue: [
32 | 'bg-blue-50',
33 | 'text-blue-700',
34 | '[&>.badge-title]:bg-blue-700',
35 | '[&>.badge-title]:text-white'
36 | ],
37 | red: [
38 | 'bg-red-50',
39 | 'text-red-700',
40 | '[&>.badge-title]:bg-red-700',
41 | '[&>.badge-title]:text-white'
42 | ],
43 | orange: [
44 | 'bg-orange-50',
45 | 'text-orange-700',
46 | '[&>.badge-title]:bg-orange-700',
47 | '[&>.badge-title]:text-white'
48 | ],
49 | gray: [
50 | 'bg-gray-50',
51 | 'text-gray-700',
52 | '[&>.badge-title]:bg-gray-700',
53 | '[&>.badge-title]:text-white'
54 | ]
55 | },
56 | size: {
57 | sm: [
58 | 'text-xs',
59 | 'px-[4px]',
60 | 'py-[1px]',
61 | '[&>*]:px-1',
62 | '[&>*]:py-[1px]',
63 | '[&>*]:my-[2px]'
64 | ],
65 | md: [
66 | 'text-sm',
67 | 'px-[4px]',
68 | 'py-[1px]',
69 | '[&>*]:px-2',
70 | '[&>*]:py-[1px]',
71 | '[&>*]:my-[2px]'
72 | ],
73 | lg: [
74 | 'text-sm',
75 | 'px-[6px]',
76 | 'py-[2px]',
77 | '[&>*]:px-2',
78 | '[&>*]:py-[2px]',
79 | '[&>*]:my-[3px]'
80 | ]
81 | }
82 | },
83 | defaultVariants: {
84 | variant: 'primary',
85 | size: 'md'
86 | }
87 | }
88 | );
89 |
90 | type BadgeBaseProps = VariantProps;
91 | interface Props extends BadgeBaseProps {
92 | children: ReactNode;
93 | className?: string;
94 | }
95 |
96 | export function Badge({ children, className, size, variant }: Props) {
97 | return (
98 |
99 | {typeof children == 'string' ? {children} : children}
100 |
101 | );
102 | }
103 |
104 | Badge.Title = function BadgeTitle({ children }: { children: string }) {
105 | return {children} ;
106 | };
107 |
108 | Badge.Content = function BadgeContent({ children }: { children: string }) {
109 | return {children} ;
110 | };
111 |
--------------------------------------------------------------------------------
/src/components/BlogCard.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image';
2 | import Link from 'next/link';
3 | import React from 'react';
4 | import { ArrowUpRight } from 'react-feather';
5 |
6 | interface Props {
7 | children: React.ReactNode;
8 | href?: string;
9 | }
10 |
11 | export function BlogCard({ children }: Props) {
12 | return {children} ;
13 | }
14 |
15 | BlogCard.Title = function Title({
16 | children,
17 | href = '/',
18 | ref
19 | }: Props & { ref?: any }) {
20 | return (
21 |
22 | {children}
23 |
24 |
25 | );
26 | };
27 |
28 | BlogCard.Description = function Description({ children }: Props) {
29 | return {children}
;
30 | };
31 |
32 | BlogCard.Image = function Images({
33 | source
34 | }: {
35 | source: { src: string; alt: string };
36 | }) {
37 | return (
38 |
39 |
46 |
47 | );
48 | };
49 |
50 | BlogCard.Information = function Information({
51 | author,
52 | date
53 | }: {
54 | author: string;
55 | date: string;
56 | }) {
57 | return (
58 |
59 | {author}
60 | •
61 | {date}
62 |
63 | );
64 | };
65 |
--------------------------------------------------------------------------------
/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import { cva, VariantProps } from 'class-variance-authority';
2 | import React from 'react';
3 |
4 | const buttonStyles = cva(
5 | ['font-semibold', 'rounded-lg', 'border', 'shadow-sm', 'shadow-violet-100/5'],
6 | {
7 | variants: {
8 | variant: {
9 | filled: [
10 | 'text-white',
11 | 'bg-blue-600',
12 | 'border-blue-600',
13 | 'hover:bg-blue-700',
14 | 'focus:ring-4',
15 | 'ring-blue-100',
16 | 'disabled:bg-blue-200',
17 | 'disabled:border-blue-200',
18 | 'disabled:cursor-not-allowed'
19 | ],
20 | outline: [
21 | 'text-black',
22 | 'bg-white',
23 | 'border-gray-300',
24 | 'hover:border-gray-400',
25 | 'focus:ring-4',
26 | 'ring-gray-100',
27 | 'disabled:bg-gray-200',
28 | 'disabled:cursor-not-allowed'
29 | ]
30 | },
31 | fullWidth: {
32 | true: 'w-full'
33 | },
34 | size: {
35 | sm: ['text-sm', 'px-[14px]', 'py-2'],
36 | md: ['text-sm', 'px-4', 'py-[10px]'],
37 | lg: ['px-[18px]', 'py-[10px]'],
38 | xl: ['px-5', 'py-3'],
39 | '2xl': ['text-lg', 'px-7', 'py-4']
40 | }
41 | },
42 | defaultVariants: {
43 | variant: 'filled',
44 | size: 'md'
45 | }
46 | }
47 | );
48 |
49 | // TODO: Icon
50 | type ButtonBaseProps = VariantProps;
51 | interface Props
52 | extends React.ButtonHTMLAttributes,
53 | ButtonBaseProps {}
54 |
55 | export const Button = ({
56 | className,
57 | variant,
58 | fullWidth,
59 | size,
60 | ...props
61 | }: Props) => (
62 |
66 | );
67 |
--------------------------------------------------------------------------------
/src/components/Container.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Container = ({ children }: { children: React.ReactNode }) => {
4 | return ;
5 | };
6 |
--------------------------------------------------------------------------------
/src/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 |
3 | import { FacebookIcon, LinkedinIcon, TwitterIcon } from '@/assets/icons';
4 | import { FooterRoutes } from '@/data/navigation';
5 |
6 | export const Footer = () => {
7 | return (
8 |
9 |
10 |
11 | LOGO
12 |
13 |
14 |
15 | {FooterRoutes?.map(({ id, routes }) => (
16 |
17 | {routes?.map(({ id, name, href }) => (
18 |
22 | {name}
23 |
24 | ))}
25 |
26 | ))}
27 |
28 |
29 |
30 |
31 |
32 | © 2077 Untitled UI. All rights reserved.
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | );
56 | };
57 |
--------------------------------------------------------------------------------
/src/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 | import { useRouter } from 'next/router';
3 | import { useState } from 'react';
4 | import { Menu, X } from 'react-feather';
5 |
6 | import { DiscordIcon } from '@/assets/icons';
7 | import { Button } from '@/components';
8 | import { HeaderLinks } from '@/data/navigation';
9 |
10 | export const Header = () => {
11 | const [isMenuOpen, setIsMenuOpen] = useState(false);
12 | const router = useRouter();
13 |
14 | return (
15 |
71 | );
72 | };
73 |
--------------------------------------------------------------------------------
/src/components/Hero.tsx:
--------------------------------------------------------------------------------
1 | import { cva } from 'class-variance-authority';
2 | import React from 'react';
3 |
4 | const heroTitle = cva(['mb-6 font-semibold text-gray-900'], {
5 | variants: {
6 | size: {
7 | lg: 'md:text-6xl text-4xl',
8 | md: 'md:text-5xl text-4xl',
9 | sm: 'md:text-4xl text-3xl'
10 | }
11 | },
12 | defaultVariants: {
13 | size: 'sm'
14 | }
15 | });
16 |
17 | export function Hero({ children }: { children: React.ReactNode }) {
18 | return (
19 |
22 | );
23 | }
24 |
25 | Hero.Label = function HeroTitle({ children }: { children: React.ReactNode }) {
26 | return (
27 |
28 | {children}
29 |
30 | );
31 | };
32 |
33 | Hero.Title = function HeroTitle({
34 | children,
35 | size
36 | }: {
37 | children: React.ReactNode;
38 | size?: 'md' | 'lg' | 'sm';
39 | }) {
40 | return {children} ;
41 | };
42 |
43 | Hero.Subtitle = function HeroSubtitle({
44 | children
45 | }: {
46 | children: React.ReactNode;
47 | }) {
48 | return (
49 |
50 | {children}
51 |
52 | );
53 | };
54 |
--------------------------------------------------------------------------------
/src/components/IconButton.tsx:
--------------------------------------------------------------------------------
1 | import { cva, VariantProps } from 'class-variance-authority';
2 | import { ButtonHTMLAttributes } from 'react';
3 |
4 | const iconButtonStyles = cva(['flex w-fit h-fit'], {
5 | variants: {
6 | variant: {
7 | default: [],
8 | gray: [
9 | 'text-gray-400 fill-current hover:text-gray-500 active:text-gray-600'
10 | ],
11 | bordered: [
12 | 'border border-gray-200 hover:border-gray-400 active:border-gray-600',
13 | 'rounded-lg',
14 | 'shadow-sm shadow-blue-900/5'
15 | ]
16 | },
17 | padding: {
18 | none: 'p-0',
19 | sm: 'p-1',
20 | md: 'p-2',
21 | lg: 'p-3'
22 | }
23 | },
24 | defaultVariants: {
25 | variant: 'default',
26 | padding: 'md'
27 | }
28 | });
29 |
30 | type IconButtonBaseProps = VariantProps;
31 | interface Props
32 | extends ButtonHTMLAttributes,
33 | IconButtonBaseProps {}
34 |
35 | export const IconButton = ({
36 | className,
37 | variant,
38 | padding,
39 | ...props
40 | }: Props) => (
41 |
45 | );
46 |
--------------------------------------------------------------------------------
/src/components/Input.tsx:
--------------------------------------------------------------------------------
1 | import { cx } from 'class-variance-authority';
2 | import React from 'react';
3 | import { AlertCircle } from 'react-feather';
4 |
5 | const baseClasses = cx([
6 | 'py-2.5 px-3.5 border transition-all',
7 | 'text-gray-800 shadow-sm rounded-lg border-gray-300 placeholder:text-gray-500',
8 | 'focus:outline-none focus:ring-2 focus:ring-blue-300 focus:border-blue-300',
9 | 'disabled:opacity-50 disabled:cursor-not-allowed w-full'
10 | ]);
11 | const errorClasses = '!border-red-300 focus:ring-2 focus:ring-red-100';
12 |
13 | interface Props extends React.InputHTMLAttributes {
14 | label?: string;
15 | errorMessage?: string;
16 | }
17 |
18 | const ErrorField = ({ errorMessage }: { errorMessage?: string }) => {
19 | return {errorMessage}
;
20 | };
21 |
22 | export const Input: React.FC = ({ errorMessage, label, ...props }) => {
23 | return (
24 |
25 | {label && (
26 |
27 | {label}
28 |
29 | )}
30 |
31 |
35 | {errorMessage && (
36 |
37 |
38 |
39 | )}
40 |
41 | {errorMessage &&
}
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/components/Pagination/Pagination.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { ArrowLeft, ArrowRight } from 'react-feather';
3 |
4 | import {
5 | PaginationButtonGroupProps,
6 | PaginationButtonProps,
7 | PaginationGroupItem,
8 | PaginationNumberButtonProps,
9 | PaginationProps
10 | } from '@/components/Pagination/Pagination.types';
11 | import {
12 | calculatePagination,
13 | mapPagesToPaginationGroupItems
14 | } from '@/utils/pagination.util';
15 |
16 | const ArrowButton: React.FC = ({
17 | onPageClick,
18 | position,
19 | content,
20 | show
21 | }) => {
22 | const positionClasses: Record = {
23 | left: 'justify-start',
24 | right: 'justify-end'
25 | };
26 | return (
27 |
28 | {show && (
29 | onPageClick()}
32 | >
33 | {content}
34 |
35 | )}
36 |
37 | );
38 | };
39 |
40 | const PaginationNumberButton: React.FC = ({
41 | onPageClick,
42 | page,
43 | active
44 | }) => {
45 | return (
46 | (onPageClick ? onPageClick() : null)}
51 | >
52 | {page}
53 |
54 | );
55 | };
56 |
57 | const PaginationButtonGroup: React.FC = ({
58 | onPageClick,
59 | pages,
60 | maxPage,
61 | current
62 | }) => {
63 | const [pageItems, setPageItems] = useState([]);
64 | const [totalCount, setTotalCount] = useState(pages.length);
65 | const [centerIndex, setCenterIndex] = useState(0);
66 |
67 | useEffect(() => {
68 | const index = pages.findIndex(p => p === current);
69 | setCenterIndex(index);
70 | setTotalCount(pages.length);
71 | }, [pages, current]);
72 |
73 | useEffect(() => {
74 | setPageItems(
75 | mapPagesToPaginationGroupItems({
76 | maxPage,
77 | pages,
78 | centerIndex: centerIndex,
79 | totalCount
80 | })
81 | );
82 | }, [centerIndex, totalCount, pages, maxPage]);
83 |
84 | return (
85 |
86 | {pageItems.map((item, index) => (
87 |
onPageClick(item.page)}
92 | />
93 | ))}{' '}
94 |
95 | );
96 | };
97 |
98 | const Pagination: React.FC = ({
99 | onPageClick,
100 | current,
101 | total,
102 | limit = 10
103 | }) => {
104 | const [isPrevAvailable, setIsPrevAvailable] = useState(false);
105 | const [isNextAvailable, setIsNextAvailable] = useState(false);
106 | const [maxPage, setMaxPage] = useState(0);
107 | const [pages, setPages] = useState([current]);
108 |
109 | useEffect(() => {
110 | setIsPrevAvailable(current > 1);
111 | setIsNextAvailable(current * limit < total);
112 | setMaxPage(Math.ceil(total / limit));
113 | setPages(
114 | calculatePagination({
115 | maxPage: maxPage,
116 | minPage: 1,
117 | current: current
118 | })
119 | );
120 | }, [maxPage, limit, total, current]);
121 |
122 | return (
123 |
124 |
onPageClick(current - 1)}
128 | content={
129 | <>
130 | Önceki
131 | >
132 | }
133 | />{' '}
134 | onPageClick(page)}
139 | />{' '}
140 | onPageClick(current + 1)}
144 | content={
145 | <>
146 | Sonraki
147 | >
148 | }
149 | />
150 |
151 | );
152 | };
153 |
154 | export default Pagination;
155 |
--------------------------------------------------------------------------------
/src/components/Pagination/Pagination.types.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export type Pagination = {
4 | current: number;
5 | limit?: number;
6 | total: number;
7 | };
8 |
9 | // eslint-disable-next-line no-unused-vars
10 | type PaginationCallback = (current: number) => void;
11 |
12 | export type PaginationProps = Pagination & {
13 | onPageClick: PaginationCallback;
14 | };
15 |
16 | export type PaginationButtonProps = {
17 | content: React.ReactNode;
18 | show: boolean;
19 | position: 'left' | 'right';
20 | onPageClick: () => void;
21 | };
22 |
23 | export type PaginationButtonGroupProps = {
24 | pages: number[];
25 | current: number;
26 | maxPage: number;
27 | onPageClick: PaginationCallback;
28 | };
29 |
30 | export type PaginationNumberButtonProps = {
31 | page: number | string;
32 | active: boolean;
33 | onPageClick?: () => void;
34 | };
35 |
36 | export type PaginationGroupItem = {
37 | content: number | string;
38 | show: boolean;
39 | page: number;
40 | };
41 |
--------------------------------------------------------------------------------
/src/components/Quote/QuoteSlider.tsx:
--------------------------------------------------------------------------------
1 | import 'swiper/css';
2 | import 'swiper/css/navigation';
3 |
4 | import { ArrowLeft, ArrowRight } from 'react-feather';
5 | import { Navigation } from 'swiper';
6 | import { Swiper, SwiperSlide } from 'swiper/react';
7 |
8 | import { Avatar } from '@/components';
9 | import Quotes from '@/data/quotes';
10 | import type { Quote } from '@/types';
11 |
12 | const QuoteSlider = () => {
13 | return (
14 | <>
15 |
20 | {Quotes.map(quote => (
21 |
22 |
23 |
24 | ))}
25 |
26 |
27 | >
28 | );
29 | };
30 |
31 | const SliderNavigations = () => (
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | );
41 |
42 | const QuoteItem = ({ quote }: { quote: Quote }) => (
43 |
44 |
{quote.content}
45 |
46 |
{quote.author.name}
47 |
{quote.author.title}
48 |
49 | );
50 |
51 | QuoteSlider.Item = QuoteItem;
52 | QuoteSlider.Navigations = SliderNavigations;
53 |
54 | export default QuoteSlider;
55 |
--------------------------------------------------------------------------------
/src/components/TeamCard.tsx:
--------------------------------------------------------------------------------
1 | import Image from 'next/image';
2 | import Link from 'next/link';
3 | import { ReactNode } from 'react';
4 | import { GitHub, Linkedin, Twitter } from 'react-feather';
5 |
6 | export const TeamCard = ({ children }: { children: ReactNode }) => {
7 | return {children}
;
8 | };
9 |
10 | const TeamCardImage = ({ src, alt }: { src: string; alt: string }) => {
11 | return (
12 |
13 |
20 |
21 | );
22 | };
23 |
24 | const TeamCardTitle = ({ children }: { children: ReactNode }) => {
25 | return (
26 |
27 | {children}
28 |
29 | );
30 | };
31 | const TeamCardSubTitle = ({ children }: { children: ReactNode }) => {
32 | return (
33 |
34 | {children}
35 |
36 | );
37 | };
38 | const TeamCardDescription = ({ children }: { children: ReactNode }) => {
39 | return (
40 |
41 | {children}
42 |
43 | );
44 | };
45 |
46 | interface ISocialLinks {
47 | socialLinks: {
48 | twitter: string;
49 | linkedin: string;
50 | github: string;
51 | };
52 | }
53 |
54 | const TeamCardSocial = ({ socialLinks }: ISocialLinks) => {
55 | return (
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | TeamCard.Image = TeamCardImage;
71 | TeamCard.Title = TeamCardTitle;
72 | TeamCard.SubTitle = TeamCardSubTitle;
73 | TeamCard.Description = TeamCardDescription;
74 | TeamCard.Social = TeamCardSocial;
75 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | export { TeamCard } from './TeamCard';
2 | export { AuthorCard } from './AuthorCard';
3 | export { BlogCard } from './BlogCard';
4 | export { IconButton } from './IconButton';
5 |
6 | export { Hero } from './Hero';
7 | export { Avatar } from './Avatar';
8 | export { Container } from './Container';
9 | export { Footer } from './Footer';
10 | export { Header } from './Header';
11 | export { Button } from './Button';
12 | export { Input } from './Input';
13 | export { Badge } from './Badge';
14 |
--------------------------------------------------------------------------------
/src/components/sections/FaqSection.tsx:
--------------------------------------------------------------------------------
1 | import { Disclosure } from '@headlessui/react';
2 | import React from 'react';
3 | import { MinusCircle, PlusCircle } from 'react-feather';
4 |
5 | import { Container, Hero } from '@/components';
6 | import { FaqList } from '@/data/faq';
7 |
8 | export const FaqSection: React.FC = () => {
9 | return (
10 |
11 |
12 | Frequently asked questions
13 |
14 | Everything you need to know about the product and billing.
15 |
16 |
17 | {FaqList.map(({ id, title, description }) => (
18 |
19 | {({ open }) => (
20 | <>
21 |
22 |
23 | {title}
24 |
25 | {open ? (
26 |
27 | ) : (
28 |
29 | )}
30 |
31 |
32 |
33 | {description}
34 |
35 |
36 | >
37 | )}
38 |
39 | ))}
40 |
41 |
42 |
43 | );
44 | };
45 |
--------------------------------------------------------------------------------
/src/components/sections/FeaturesSection.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Hero } from '@/components';
2 | import Features from '@/data/features';
3 |
4 | export const FeaturesSection = () => {
5 | return (
6 |
7 |
8 |
9 | Features
10 | Frontendship’te ne yapıyoruz?
11 |
12 | Her seviyeden geliştiricinin potansiyelini artırmak adına topluluğa
13 | ve ekosisteme faydalı projeler ve içerikler sunmayı hedefliyoruz.
14 |
15 |
16 | {Features.map(({ id, icon: FeatureIcon, title, description }) => (
17 |
18 |
19 |
20 |
21 |
22 | {title}
23 |
24 | {description}
25 |
26 | ))}
27 |
28 |
29 |
30 |
31 | );
32 | };
33 |
--------------------------------------------------------------------------------
/src/components/sections/MetricsSection.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Hero } from '@/components';
2 |
3 | export const MetricsSection = () => {
4 | return (
5 |
6 |
7 | Verilerle Topluluk
8 | Topluluğumuzu verilerle tanıyın,
9 |
33 |
34 |
35 | );
36 | };
37 |
--------------------------------------------------------------------------------
/src/components/sections/QuotesSection.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import QuoteSlider from '../Quote/QuoteSlider';
4 |
5 | export const QuotesSection = () => {
6 | return (
7 |
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/src/components/sections/TechnologiesSection.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Container, Hero } from '@/components';
2 | import Technologies from '@/data/technologies';
3 |
4 | export const TechnologiesSection = () => {
5 | return (
6 |
7 |
8 |
9 | Teknoloji ve Araçlar
10 |
11 | Toplulukta, geliştirme sürecinin her aşamasına dokunan teknoloji ve
12 | araçlar tartışılıp, deneyimler paylaşılmaktadır.
13 |
14 |
15 |
16 | {Technologies.map(({ id, icon: TechnologyIcon, name }) => (
17 |
18 |
19 |
20 | ))}
21 |
22 |
23 | View All Technologies
24 |
25 |
26 |
27 | );
28 | };
29 |
--------------------------------------------------------------------------------
/src/content/blog/oguz-blog-test.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Create Menu with Headless UI
3 | description: Learn how to build menu component with Vue.js and Headless UI
4 | author: Oguz Ergul
5 | cover: https://i.ytimg.com/vi/HIJDZxwcPUw/maxresdefault.jpg
6 | avatar: https://pbs.twimg.com/profile_images/1524303338304679937/yd_58miQ_400x400.jpg
7 | date: '22 Aug 2022'
8 | category: Engineering
9 | tags:
10 | - Vue.js
11 | - Headless UI
12 | - Tailwind CSS
13 | content:
14 | - name: Introduction
15 | slug: introduction
16 | - name: Software and tools
17 | slug: software-and-tools
18 | - name: Other resources
19 | slug: other-resources
20 | - name: Conclusion
21 | slug: conclusion
22 | ---
23 |
24 |
25 | ## Introduction
26 |
27 | Mi tincidunt elit, id quisque ligula ac diam, amet. Vel etiam suspendisse morbi eleifend faucibus eget vestibulum felis. Dictum quis montes, sit sit. Tellus aliquam enim urna, etiam. Mauris posuere vulputate arcu amet, vitae nisi, tellus tincidunt. At feugiat sapien varius id.
28 |
29 | Eget quis mi enim, leo lacinia pharetra, semper. Eget in volutpat mollis at volutpat lectus velit, sed auctor. Porttitor fames arcu quis fusce augue enim. Quis at habitant diam at. Suscipit tristique risus, at donec. In turpis vel et quam imperdiet. Ipsum molestie aliquet sodales id est ac volutpat.
30 |
31 | Dolor enim eu tortor urna sed duis nulla. Aliquam vestibulum, nulla odio nisl vitae. In aliquet pellentesque aenean hac vestibulum turpis mi bibendum diam. Tempor integer aliquam in vitae malesuada fringilla.
32 |
33 | Elit nisi in eleifend sed nisi. Pulvinar at orci, proin imperdiet commodo consectetur convallis risus. Sed condimentum enim dignissim adipiscing faucibus consequat, urna. Viverra purus et erat auctor aliquam. Risus, volutpat vulputate posuere purus sit congue convallis aliquet. Arcu id augue ut feugiat donec porttitor neque. Mauris, neque ultricies eu vestibulum, bibendum quam lorem id. Dolor lacus, eget nunc lectus in tellus, pharetra, porttitor.
34 |
35 | Ipsum sit mattis nulla quam nulla. Gravida id gravida ac enim mauris id. Non pellentesque congue eget consectetur turpis. Sapien, dictum molestie sem tempor. Diam elit, orci, tincidunt aenean tempus. Quis velit eget ut tortor tellus. Sed vel, congue felis elit erat nam nibh orci.
36 |
37 |
38 | ```jsx {3,5} showLineNumbers
39 | import React from 'react'
40 |
41 | const MyComponent = () => {
42 | return (
43 |
44 |
My Component
45 |
My component is awesome
46 |
47 | )
48 | }
49 | ```
50 |
51 | > In a world older and more complete than ours they move finished and complete, gifted with extensions of the senses we have lost or never attained, living by voices we shall never hear.
52 |
53 | — Olivia Rhye, Product Designer
54 |
55 | ## Software and tools
56 |
57 | Pharetra morbi libero id aliquam elit massa integer tellus. Quis felis aliquam ullamcorper porttitor. Pulvinar ullamcorper sit dictumst ut eget a, elementum eu. Maecenas est morbi mattis id in ac pellentesque ac.
58 |
59 | ## Other resources
60 |
61 | Sagittis et eu at elementum, quis in. Proin praesent volutpat egestas sociis sit lorem nunc nunc sit. Eget diam curabitur mi ac. Auctor rutrum lacus malesuada massa ornare et. Vulputate consectetur ac ultrices at diam dui eget fringilla tincidunt. Arcu sit dignissim massa erat cursus vulputate gravida id. Sed quis auctor vulputate hac elementum gravida cursus dis.
62 |
63 |
64 | 1. Lectus id duis vitae porttitor enim [gravida morbi](https://duckduckgo.com).
65 | 2. Eu turpis posuere semper feugiat volutpat elit, ultrices suspendisse. Auctor vel in vitae placerat.
66 | 3. Suspendisse maecenas ac donec scelerisque diam sed est duis purus.
67 |
68 |
--------------------------------------------------------------------------------
/src/data/faq.ts:
--------------------------------------------------------------------------------
1 | import type { Faq } from 'types';
2 |
3 | export const FaqList: Faq[] = [
4 | {
5 | id: 1,
6 | title: 'Is there a free trial available?',
7 | description:
8 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.'
9 | },
10 | {
11 | id: 2,
12 | title: 'Can I change my plan later?',
13 | description:
14 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.'
15 | },
16 | {
17 | id: 3,
18 | title: 'What is your cancellation policy?',
19 | description:
20 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.'
21 | },
22 | {
23 | id: 4,
24 | title: 'Can other info be added to an invoice?',
25 | description:
26 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.'
27 | },
28 | {
29 | id: 5,
30 | title: 'How does billing work?',
31 | description:
32 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.'
33 | },
34 | {
35 | id: 6,
36 | title: 'How do I change my account email?',
37 | description:
38 | 'Yes, you can try us for free for 30 days. If you want, we’ll provide you with a free, personalized 30-minute onboarding call to get you up and running as soon as possible.'
39 | }
40 | ];
41 |
--------------------------------------------------------------------------------
/src/data/features.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BarChart2,
3 | Command,
4 | Mail,
5 | MessageCircle,
6 | Smile,
7 | Zap
8 | } from 'react-feather';
9 | import type { Feature } from 'types';
10 |
11 | const Features: Feature[] = [
12 | {
13 | id: 1,
14 | title: 'Soru Cevap',
15 | description:
16 | 'Whether you have a team of 2 or 200, our shared team inboxes keep everyone on the same page and in the loop.',
17 | icon: Mail
18 | },
19 | {
20 | id: 2,
21 | title: 'Gönüllülük',
22 | description:
23 | 'An all-in-one customer service platform that helps you balance everything your customers need to be happy.',
24 | icon: Zap
25 | },
26 | {
27 | id: 3,
28 | title: 'Açık Kaynak',
29 | description:
30 | 'An all-in-one customer service platform that helps you balance everything your customers need to be happy.',
31 | icon: BarChart2
32 | },
33 | {
34 | id: 4,
35 | title: 'Etkileşim',
36 | description:
37 | 'Measure what matters with Untitled’s easy-to-use reports. You can filter, export, and drilldown on the data in a couple clicks.',
38 | icon: Smile
39 | },
40 | {
41 | id: 5,
42 | title: 'Deneyim',
43 | description:
44 | 'Solve a problem or close a sale in real-time with chat. If no one is available, customers are seamlessly routed to email without confusion.',
45 | icon: Command
46 | },
47 | {
48 | id: 6,
49 | title: 'Feedback',
50 | description:
51 | 'Explore 100+ integrations that make your day-to-day workflow more efficient and familiar. Plus, our extensive developer tools.',
52 | icon: MessageCircle
53 | }
54 | ];
55 |
56 | export default Features;
57 |
--------------------------------------------------------------------------------
/src/data/navigation.ts:
--------------------------------------------------------------------------------
1 | import { FooterLink, Link } from 'types';
2 |
3 | export const FooterRoutes: FooterLink[] = [
4 | {
5 | id: 1,
6 | section: 'Product',
7 | routes: [
8 | {
9 | id: 1,
10 | name: 'Product',
11 | href: '#'
12 | },
13 | {
14 | id: 2,
15 | name: 'Overview',
16 | href: '#'
17 | },
18 | {
19 | id: 3,
20 | name: 'Features',
21 | href: '#'
22 | },
23 | {
24 | id: 4,
25 | name: 'Solutions',
26 | href: '#'
27 | },
28 | {
29 | id: 5,
30 | name: 'Tutorials',
31 | href: '#'
32 | },
33 | {
34 | id: 6,
35 | name: 'Pricing',
36 | href: '#'
37 | },
38 | {
39 | id: 7,
40 | name: 'Releases',
41 | href: '#'
42 | }
43 | ]
44 | },
45 | {
46 | id: 2,
47 | section: 'Company',
48 | routes: [
49 | {
50 | id: 1,
51 | name: 'About us',
52 | href: '#'
53 | },
54 | {
55 | id: 2,
56 | name: 'Careers',
57 | href: '#'
58 | },
59 | {
60 | id: 3,
61 | name: 'Press',
62 | href: '#'
63 | },
64 | {
65 | id: 4,
66 | name: 'News',
67 | href: '#'
68 | },
69 | {
70 | id: 5,
71 | name: 'Media Kit',
72 | href: '#'
73 | },
74 | {
75 | id: 6,
76 | name: 'Contact',
77 | href: '#'
78 | }
79 | ]
80 | },
81 | {
82 | id: 3,
83 | section: 'Resources',
84 | routes: [
85 | {
86 | id: 1,
87 | name: 'Blog',
88 | href: '#'
89 | },
90 | {
91 | id: 2,
92 | name: 'Newsletter',
93 | href: '#'
94 | },
95 | {
96 | id: 3,
97 | name: 'Events',
98 | href: '#'
99 | },
100 | {
101 | id: 4,
102 | name: 'Help Center',
103 | href: '#'
104 | },
105 | {
106 | id: 5,
107 | name: 'Tutorials',
108 | href: '#'
109 | },
110 | {
111 | id: 6,
112 | name: 'Support',
113 | href: '#'
114 | }
115 | ]
116 | }
117 | ];
118 |
119 | export const HeaderLinks: Link[] = [
120 | {
121 | id: 1,
122 | name: 'FRONTENDSHIP',
123 | accessibleName: 'Anasayfa',
124 | href: '#'
125 | },
126 | {
127 | id: 2,
128 | name: 'Anasayfa',
129 | href: '/'
130 | },
131 | {
132 | id: 3,
133 | name: 'Hakkımızda',
134 | href: '/about'
135 | },
136 | {
137 | id: 4,
138 | name: 'Blog',
139 | href: '/blog'
140 | }
141 | ];
142 |
--------------------------------------------------------------------------------
/src/data/quotes.ts:
--------------------------------------------------------------------------------
1 | import type { Quote } from 'types';
2 |
3 | const Quotes: Quote[] = [
4 | {
5 | id: 1,
6 | content:
7 | 'Untitled has saved us thousands of hours of work and has unlock data insights we never thought possible.',
8 | author: {
9 | name: 'Koray Okumuş',
10 | title: 'UX Designer, Circooles',
11 | avatar: '/avatars/Avatar.png'
12 | }
13 | },
14 | {
15 | id: 2,
16 | content:
17 | 'Untitled has saved us thousands of hours of work and has unlock data insights we never thought possible.',
18 | author: {
19 | name: 'Koray Okumuş',
20 | title: 'UX Designer, Circooles',
21 | avatar: '/avatars/Avatar.png'
22 | }
23 | },
24 | {
25 | id: 3,
26 | content:
27 | 'Untitled has saved us thousands of hours of work and has unlock data insights we never thought possible.',
28 | author: {
29 | name: 'Koray Okumuş',
30 | title: 'UX Designer, Circooles',
31 | avatar: '/avatars/Avatar.png'
32 | }
33 | }
34 | ];
35 |
36 | export default Quotes;
37 |
--------------------------------------------------------------------------------
/src/data/technologies.ts:
--------------------------------------------------------------------------------
1 | import type { Technology } from 'types';
2 |
3 | import {
4 | AngularIcon,
5 | DockerIcon,
6 | FigmaIcon,
7 | GithubIcon,
8 | GitIcon,
9 | HTMLIcon,
10 | JavaScriptIcon,
11 | NodeIcon,
12 | NpmIcon,
13 | ReactIcon,
14 | ReduxIcon,
15 | RemixRunIcon,
16 | TypeScriptIcon,
17 | VisualStudioIcon,
18 | VueIcon,
19 | WebpackIcon
20 | } from '@/assets/icons';
21 |
22 | const Technologies: Technology[] = [
23 | {
24 | id: 1,
25 | icon: ReactIcon,
26 | name: 'React'
27 | },
28 | {
29 | id: 2,
30 | icon: VueIcon,
31 | name: 'Vue'
32 | },
33 | {
34 | id: 3,
35 | icon: JavaScriptIcon,
36 | name: 'JavaScript'
37 | },
38 | {
39 | id: 4,
40 | icon: GithubIcon,
41 | name: 'Github'
42 | },
43 | {
44 | id: 5,
45 | icon: DockerIcon,
46 | name: 'Docker'
47 | },
48 | {
49 | id: 6,
50 | icon: GitIcon,
51 | name: 'Git'
52 | },
53 | {
54 | id: 7,
55 | icon: RemixRunIcon,
56 | name: 'Remix Run'
57 | },
58 | {
59 | id: 8,
60 | icon: HTMLIcon,
61 | name: 'HTML'
62 | },
63 | {
64 | id: 9,
65 | icon: FigmaIcon,
66 | name: 'Figma'
67 | },
68 | {
69 | id: 10,
70 | icon: AngularIcon,
71 | name: 'Angular'
72 | },
73 | {
74 | id: 11,
75 | icon: TypeScriptIcon,
76 | name: 'TypeScript'
77 | },
78 | {
79 | id: 12,
80 | icon: NodeIcon,
81 | name: 'Node'
82 | },
83 | {
84 | id: 13,
85 | icon: VisualStudioIcon,
86 | name: 'Visual Studio'
87 | },
88 | {
89 | id: 14,
90 | icon: ReduxIcon,
91 | name: 'Redux'
92 | },
93 | {
94 | id: 15,
95 | icon: NpmIcon,
96 | name: 'Npm'
97 | },
98 | {
99 | id: 16,
100 | icon: WebpackIcon,
101 | name: 'Webpack'
102 | }
103 | ];
104 |
105 | export default Technologies;
106 |
--------------------------------------------------------------------------------
/src/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '@/styles/globals.css';
2 |
3 | import { Inter } from '@next/font/google';
4 | import type { AppProps } from 'next/app';
5 |
6 | import { Button, Footer, Header, Input } from '@/components';
7 |
8 | const inter = Inter({
9 | subsets: ['latin'],
10 | variable: '--font-inter'
11 | });
12 |
13 | export default function App({ Component, pageProps }: AppProps) {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/src/pages/about.tsx:
--------------------------------------------------------------------------------
1 | import { Container, Hero, TeamCard } from '@/components';
2 |
3 | const AboutPage = () => {
4 | return (
5 |
6 |
7 |
8 | The team
9 | Meet the team behind Frontendship
10 |
11 | We’re a small team that loves to create great experiences and make
12 | meaningful connections between builders and customers. Join our
13 | remote ream!
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 | John Doe
25 | CEO
26 |
27 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. lorem.
28 |
29 |
30 |
31 |
32 | );
33 | };
34 |
35 | export default AboutPage;
36 |
--------------------------------------------------------------------------------
/src/pages/api/hello.ts:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 | import type { NextApiResponse } from 'next';
3 |
4 | type Data = {
5 | name: string;
6 | };
7 |
8 | export default function handler(res: NextApiResponse) {
9 | res.status(200).json({ name: 'John Doe' });
10 | }
11 |
--------------------------------------------------------------------------------
/src/pages/blog/[slug].tsx:
--------------------------------------------------------------------------------
1 | import Link from 'next/link';
2 | import { MDXRemote } from 'next-mdx-remote';
3 |
4 | import {
5 | GithubLinkIcon,
6 | LinkedinIcon,
7 | LinkIcon,
8 | TwitterIcon
9 | } from '@/assets/icons';
10 | import { AuthorCard, Badge, Container, Hero, IconButton } from '@/components';
11 | import { GetAllSlugs, GetNoteBySlug } from '@/utils/blog';
12 |
13 | export default function BlogDetail({
14 | source,
15 | meta: { title, category, readTime, date, avatar, author, content }
16 | }: any) {
17 | return (
18 |
19 |
20 |
21 | {date}
22 | {title}
23 |
24 | {readTime}
25 | {category}
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Table of contents
36 |
37 |
38 | {content.map(
39 | ({ slug, name }: { slug: string; name: string }) => (
40 |
44 | {name}
45 |
46 | )
47 | )}
48 |
49 |
50 |
51 |
52 |
53 | Contributors
54 |
55 |
56 |
62 |
63 | Oğuz Ergül
64 | Software Developer
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
90 |
91 |
92 | );
93 | }
94 | export async function getStaticPaths() {
95 | try {
96 | const slugs = GetAllSlugs();
97 | const paths = slugs.map((slug: any) => ({ params: { slug } }));
98 |
99 | return {
100 | paths,
101 | fallback: false
102 | };
103 | } catch (error) {
104 | return { notFound: true };
105 | }
106 | }
107 |
108 | export async function getStaticProps(context: { params: { slug: any } }) {
109 | const {
110 | params: { slug }
111 | } = context;
112 |
113 | const { meta, source } = await GetNoteBySlug(slug);
114 |
115 | return {
116 | props: {
117 | meta,
118 | source
119 | }
120 | };
121 | }
122 |
--------------------------------------------------------------------------------
/src/pages/blog/index.tsx:
--------------------------------------------------------------------------------
1 | import { BlogCard, Container, Hero } from '@/components';
2 | import { GetAllPostsMeta } from '@/utils/blog';
3 |
4 | export default function BlogPage({ posts }: { posts: any[] }) {
5 | return (
6 |
7 |
8 | Our blog
9 | Stories and interviews
10 |
11 | Subscribe to learn about new product features, the latest in
12 | technology, solutions, and updates.
13 |
14 |
15 |
16 |
17 | {posts.map(
18 | ({ meta: { title, description, cover, author, date, slug } }) => (
19 |
20 |
26 | {title}
27 | {description}
28 |
29 |
30 | )
31 | )}
32 |
33 |
34 | );
35 | }
36 |
37 | export async function getStaticProps() {
38 | const posts = GetAllPostsMeta();
39 |
40 | return {
41 | props: {
42 | posts
43 | }
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { ArrowRight, PlayCircle } from 'react-feather';
2 |
3 | import { Badge, Button, Container, Hero } from '@/components';
4 | import { FaqSection } from '@/components/sections/FaqSection';
5 | import { FeaturesSection } from '@/components/sections/FeaturesSection';
6 | import { MetricsSection } from '@/components/sections/MetricsSection';
7 | import { QuotesSection } from '@/components/sections/QuotesSection';
8 | import { TechnologiesSection } from '@/components/sections/TechnologiesSection';
9 |
10 | export default function Home() {
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 | New feature
18 | Check out the team dashboard
19 |
20 |
21 |
22 | Beautiful analytics to grow smarter
23 |
24 |
25 | Powerful, self-serve product and growth analytics to help you
26 | convert, engage, and retain more users. Trusted by over 4,000
27 | startups.
28 |
29 |
33 |
34 | Demo
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | >
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/src/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { Icon } from 'react-feather';
2 | export interface Quote {
3 | id: number;
4 | content: string;
5 | author: {
6 | name: string;
7 | title: string;
8 | avatar: string;
9 | };
10 | }
11 |
12 | export interface Feature {
13 | id: number;
14 | title: string;
15 | icon: Icon;
16 | description: string;
17 | }
18 |
19 | export interface Technology {
20 | id: number;
21 | name: string;
22 |
23 | icon: Icon;
24 | }
25 | export interface Faq {
26 | id: number;
27 | title: string;
28 | description: string;
29 | }
30 | export interface FooterLink {
31 | id: number;
32 | section?: string;
33 | routes?: {
34 | id: number;
35 | name: string;
36 | href: string;
37 | }[];
38 | }
39 |
40 | export interface Link {
41 | id: number;
42 | name?: string;
43 | href: string;
44 | accessibleName?: string;
45 | }
46 |
--------------------------------------------------------------------------------
/src/utils/blog.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import matter from 'gray-matter';
3 | import { serialize } from 'next-mdx-remote/serialize';
4 | import path from 'path';
5 | import readingTime from 'reading-time';
6 | import rehypePrettyCode from 'rehype-pretty-code';
7 | import rehypeSlug from 'rehype-slug';
8 |
9 | const RootDirectory = path.join(process.cwd(), 'src', 'content', 'blog');
10 |
11 | const GetAllPosts = () => {
12 | return fs.readdirSync(RootDirectory);
13 | };
14 |
15 | const GetPostMeta = currentSlug => {
16 | const slug = currentSlug.replace(/\.mdx$/, '');
17 | const filePath = path.join(RootDirectory, `${slug}.mdx`);
18 | const fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });
19 |
20 | const { data, content } = matter(fileContent);
21 | const readTime = readingTime(content).text;
22 |
23 | return {
24 | meta: {
25 | ...data,
26 | slug,
27 | readTime
28 | },
29 | content
30 | };
31 | };
32 |
33 | const prettyCodeOptions = {
34 | theme: 'github-light',
35 | onVisitLine(node) {
36 | if (node.children.length === 0) {
37 | node.children = [{ type: 'text', value: ' ' }];
38 | }
39 | },
40 | onVisitHighlightedLine(node) {
41 | node.properties.className.push('highlighted');
42 | },
43 | onVisitHighlightedWord(node) {
44 | node.properties.className = ['highlighted', 'word'];
45 | }
46 | };
47 |
48 | export const GetNoteBySlug = async slug => {
49 | const meta = GetPostMeta(slug);
50 | const { content } = meta;
51 |
52 | const mdxSource = await serialize(content, {
53 | mdxOptions: {
54 | rehypePlugins: [rehypeSlug, [rehypePrettyCode, prettyCodeOptions]]
55 | }
56 | });
57 |
58 | return {
59 | ...meta,
60 | source: mdxSource
61 | };
62 | };
63 |
64 | export const GetAllPostsMeta = () => {
65 | return GetAllPosts().map(file => GetPostMeta(file));
66 | };
67 |
68 | export const GetAllSlugs = () => {
69 | return GetAllPosts().map(file => file.replace(/\.mdx$/, ''));
70 | };
71 |
--------------------------------------------------------------------------------
/src/utils/pagination.util.ts:
--------------------------------------------------------------------------------
1 | import { PaginationGroupItem } from '@/components/Pagination/Pagination.types';
2 |
3 | type CalculatePaginationParams = {
4 | current: number;
5 | minPage: number;
6 | maxPage: number;
7 | maxShownPages?: number;
8 | };
9 |
10 | export const calculatePagination = (
11 | params: CalculatePaginationParams
12 | ): number[] => {
13 | const pages: number[] = [];
14 | const maxShownPages = params.maxShownPages || 5;
15 | const minPageNumber = Math.max(
16 | params.minPage,
17 | params.current - Math.floor(maxShownPages / 2)
18 | );
19 | const maxPageNumber = Math.min(
20 | params.maxPage,
21 | params.current + Math.floor(maxShownPages / 2)
22 | );
23 | const pagesCount = maxPageNumber - minPageNumber + 1;
24 | const pagesToAdd = maxShownPages - pagesCount;
25 | const minPageNumberToAdd = Math.max(
26 | params.minPage,
27 | minPageNumber - pagesToAdd
28 | );
29 | const maxPageNumberToAdd = Math.min(
30 | params.maxPage,
31 | maxPageNumber + pagesToAdd
32 | );
33 | for (let i = minPageNumberToAdd; i <= maxPageNumberToAdd; i++) {
34 | pages.push(i);
35 | }
36 | if (pages.length === 0 && params.current > 0) {
37 | pages.push(params.current);
38 | }
39 | return pages;
40 | };
41 |
42 | type MapPagesToPaginationGroupItemsParams = {
43 | pages: number[];
44 | centerIndex: number;
45 | totalCount: number;
46 | maxPage: number;
47 | };
48 |
49 | export const mapPagesToPaginationGroupItems = ({
50 | pages,
51 | centerIndex,
52 | totalCount,
53 | maxPage
54 | }: MapPagesToPaginationGroupItemsParams) => {
55 | const items: PaginationGroupItem[] = [];
56 | if (centerIndex > 1) {
57 | items.push(
58 | {
59 | content: '1',
60 | page: 1,
61 | show: false
62 | },
63 | {
64 | content: '...',
65 | page: pages[centerIndex - 2],
66 | show: true
67 | }
68 | );
69 | }
70 | pages.map((page, index) => {
71 | if (index >= centerIndex - 1 && index <= centerIndex + 1) {
72 | items.push({
73 | page,
74 | content: page,
75 | show: true
76 | });
77 | }
78 | });
79 | if (centerIndex < totalCount - 2) {
80 | items.push(
81 | {
82 | content: '...',
83 | page: pages[centerIndex + 2],
84 | show: true
85 | },
86 | {
87 | content: maxPage,
88 | page: maxPage,
89 | show: false
90 | }
91 | );
92 | }
93 | return items;
94 | };
95 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | // tailwind.config.js
2 | const { fontFamily } = require('tailwindcss/defaultTheme');
3 |
4 | /** @type {import('tailwindcss').Config} \*/
5 | module.exports = {
6 | content: [
7 | './src/pages/**/*.{js,ts,jsx,tsx}',
8 | './src/components/**/*.{js,ts,jsx,tsx}'
9 | ],
10 | theme: {
11 | extend: {
12 | fontFamily: {
13 | sans: ['var(--font-inter)', ...fontFamily.sans]
14 | }
15 | }
16 | },
17 | plugins: [require('@tailwindcss/typography')]
18 | };
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./src",
4 | "target": "es5",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "strict": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "esModuleInterop": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "jsx": "preserve",
17 | "incremental": true,
18 | "paths": {
19 | "@/*": ["./*"]
20 | }
21 | },
22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "lint-staged.config.js"],
23 | "exclude": ["node_modules"]
24 | }
25 |
--------------------------------------------------------------------------------