;
15 | export type AppDispatch = typeof store.dispatch;
16 | export default store;
17 |
--------------------------------------------------------------------------------
/apps/web/src/store/topic/topicSlice.ts:
--------------------------------------------------------------------------------
1 | import { IEntry, ITopic } from "@/types";
2 | import { createSlice } from "@reduxjs/toolkit";
3 | import { createTopic, getBySlugTopic, getLatestEntries, getPopularTopics } from "./topicThunk";
4 |
5 | interface ITopicState extends ITopic {
6 | entries: IEntry[];
7 | }
8 |
9 | const initialState = {
10 | topic: {} as ITopicState,
11 | topics: [] as ITopic[],
12 | entries: [] as IEntry[],
13 | isLoading: false,
14 | isOpenTopicModal: false,
15 | };
16 |
17 | const topicSlice = createSlice({
18 | name: "topic",
19 | initialState,
20 | reducers: {
21 | toggleTopicModal(state) {
22 | state.isOpenTopicModal = !state.isOpenTopicModal;
23 | },
24 | },
25 | extraReducers: (builder) => {
26 | builder.addCase(getBySlugTopic.pending, (state) => {
27 | state.isLoading = true;
28 | });
29 | builder.addCase(getBySlugTopic.fulfilled, (state, action) => {
30 | state.isLoading = false;
31 | state.topic = action.payload;
32 | });
33 | builder.addCase(getBySlugTopic.rejected, (state, action) => {
34 | state.isLoading = false;
35 | // state.errorMessage = action.payload;
36 | });
37 | builder.addCase(getPopularTopics.pending, (state, action) => {
38 | state.isLoading = true;
39 | });
40 | builder.addCase(getPopularTopics.fulfilled, (state, action) => {
41 | state.topics = action.payload.result;
42 | state.isLoading = false;
43 | });
44 | builder.addCase(getLatestEntries.pending, (state, action) => {
45 | state.isLoading = true;
46 | });
47 | builder.addCase(getLatestEntries.fulfilled, (state, action) => {
48 | state.entries = action.payload.result;
49 | state.isLoading = false;
50 | });
51 | builder.addCase(createTopic.fulfilled, (state, action) => {
52 | state.isOpenTopicModal = false;
53 | });
54 | },
55 | });
56 |
57 | export default topicSlice.reducer;
58 | export const { toggleTopicModal } = topicSlice.actions;
59 |
--------------------------------------------------------------------------------
/apps/web/src/store/topic/topicThunk.ts:
--------------------------------------------------------------------------------
1 | import altogic from "@/libs/altogic";
2 | import { CreateTopicData, ITopic, ResponseError } from "@/types";
3 | import getErrorTranslation from "@/utils/errors";
4 | import { createAsyncThunk } from "@reduxjs/toolkit";
5 | import { toast } from "react-toastify";
6 | import slugify from "slugify";
7 | import { RootState } from "..";
8 |
9 | export const getBySlugTopic = createAsyncThunk("topic/getBySlug", async (payload: { slug?: string }, thunkAPI) => {
10 | const endpoint = `/topics/bySlug?slug=${payload.slug}`;
11 |
12 | const { data: topicData, errors } = await altogic.endpoint.get(endpoint);
13 |
14 | if (!topicData) {
15 | toast.warning("Aradığınız konu yok veya silinmiş.");
16 | return thunkAPI.rejectWithValue("Error getting topic");
17 | }
18 |
19 | const entries = await thunkAPI.dispatch(getTopicEntries(topicData._id));
20 |
21 | return { ...topicData, entries: entries.payload.result };
22 | });
23 |
24 | export const getPopularTopics = createAsyncThunk("topic/getLatest", async () => {
25 | const endpoint = "/topics?filter=this.isPublic==true&sort=createdAt:desc";
26 |
27 | const { data: topicData, errors } = await altogic.endpoint.get(endpoint);
28 |
29 | return topicData;
30 | });
31 |
32 | export const getLatestEntries = createAsyncThunk("topic/getLatestEntries", async () => {
33 | const endpoint = "/entry?filter=this.isPublic==true&sort=createdAt:desc";
34 |
35 | const { data: entriesData } = await altogic.endpoint.get(endpoint);
36 |
37 | return entriesData;
38 | });
39 |
40 | export const createTopic = createAsyncThunk("topic/createTopic", async (payload: { values: CreateTopicData; formikActions: any }, thunkAPI) => {
41 | const { auth } = thunkAPI.getState() as RootState;
42 | const { data: topic, errors } = (await altogic.db.model("topics").create({
43 | ...payload.values,
44 | slug: slugify(payload.values.title),
45 | author: auth.user?._id,
46 | })) as { data: ITopic; errors: ResponseError | null };
47 |
48 | if (topic) {
49 | const entry = await thunkAPI.dispatch(createEntry({ ...payload.values, topic: topic._id, author: auth.user?._id }));
50 | toast.success("konu oluşturuldu yönlendiriliyorsunuz...");
51 | thunkAPI.dispatch(getPopularTopics());
52 | return { topic };
53 | } else {
54 | payload.formikActions.setErrors({ responseMessage: getErrorTranslation(errors?.items[0].code, errors?.items[0].details?.field) });
55 | thunkAPI.rejectWithValue("Error creating topic");
56 | }
57 | });
58 |
59 | const getTopicEntries = createAsyncThunk("topic/getTopicEntries", async (payload: { topic: string }, { rejectWithValue }) => {
60 | const endpoint = `/entry?filter=this.topic._id == '${payload}'`;
61 |
62 | const { data: entriesData, errors } = await altogic.endpoint.get(endpoint);
63 |
64 | if (!entriesData) {
65 | toast.warning("Aradığınız konu yok veya silinmiş.");
66 | return rejectWithValue("Error getting topic");
67 | }
68 |
69 | return entriesData;
70 | });
71 |
72 | const createEntry = createAsyncThunk("topic/createEntry", async (payload: any) => {
73 | const { data: entryData, errors } = await altogic.endpoint.post("/entry", payload);
74 | if (errors) return { errors };
75 |
76 | return entryData;
77 | });
78 |
--------------------------------------------------------------------------------
/apps/web/src/style.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,600;0,700;1,100&display=swap");
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 | .min-width {
8 | min-width: -moz-available;
9 | min-width: -webkit-fill-available;
10 | }
11 |
12 | .w-md-editor-toolbar {
13 | background: transparent;
14 | }
15 |
16 | /* Change Autocomplete styles in Chrome*/
17 | input:-webkit-autofill,
18 | input:-webkit-autofill:focus {
19 | transition: background-color 600000s 0s, color 600000s 0s;
20 | }
--------------------------------------------------------------------------------
/apps/web/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import type { APIError, ErrorEntry, User } from "altogic";
2 |
3 | export interface LoginFormData {
4 | email: string;
5 | password: string;
6 | responseMessage?: string;
7 | }
8 |
9 | export interface RegisterFormData {
10 | name: string;
11 | username: string;
12 | email: string;
13 | password: string;
14 | responseMessage?: string;
15 | }
16 |
17 | export interface CreateTopicData {
18 | title: string;
19 | content: string;
20 | responseMessage?: string;
21 | }
22 |
23 | export interface UpdateProfileData {
24 | username?: string;
25 | password?: string;
26 | passwordConfirmation?: string;
27 | }
28 |
29 | export interface ITopic {
30 | _id: string;
31 | title: string;
32 | slug: string;
33 | content: string;
34 | author?: User;
35 | createdAt: Date;
36 | updatedAt: Date;
37 | viewCount: number;
38 | entryCount: number;
39 | isPublic: boolean;
40 | }
41 |
42 | export interface IEntry {
43 | _id: string;
44 | content: string;
45 | author: User;
46 | topic: ITopic;
47 | createdAt: Date;
48 | updatedAt: Date;
49 | isPublic: boolean;
50 | }
51 |
52 | export interface IUser extends User {
53 | username: string;
54 | profilePicture?: string;
55 | }
56 |
57 | interface Item extends ErrorEntry {
58 | details?: {
59 | field?: string;
60 | };
61 | }
62 |
63 | export interface ResponseError extends APIError {
64 | items: Item[];
65 | }
66 |
--------------------------------------------------------------------------------
/apps/web/src/types/page.d.ts:
--------------------------------------------------------------------------------
1 | import { NextPage } from "next";
2 | import { ComponentType, ReactElement, ReactNode } from "react";
3 |
4 | export type Page = NextPage
& {
5 | getLayout?: (page: ReactElement) => ReactNode;
6 | layout?: ComponentType;
7 | };
8 |
--------------------------------------------------------------------------------
/apps/web/src/utils/errors.ts:
--------------------------------------------------------------------------------
1 | const errors = [
2 | {
3 | code: "email_not_verified",
4 | message: "E-posta adresiniz doğrulanmamış. Lütfen e-posta adresinizi doğrulayın.",
5 | },
6 | {
7 | code: "invalid_credentials",
8 | message: "E-posta adresiniz veya şifreniz hatalı. Lütfen bilgilerinizi kontrol edin.",
9 | },
10 | {
11 | code: "email_not_unique",
12 | message: "Bu e-posta adresi zaten kullanımda. Lütfen başka bir e-posta adresi deneyin.",
13 | },
14 | {
15 | code: "missing_required_field_value",
16 | message: "Lütfen tüm alanları doldurun.",
17 | },
18 | {
19 | code: "create_object_database_error",
20 | message: "Bir hata oluştu. Lütfen daha sonra tekrar deneyin.",
21 | },
22 | {
23 | code: "invalid_access_token",
24 | message: "Geçersiz veya süresi dolmuş erişim belirteci",
25 | },
26 | {
27 | code: "missing_access_token",
28 | message: "Erişim belirteci tanımlanamıyor",
29 | },
30 | {
31 | code: "not_unique",
32 | field: "username",
33 | message: "Bu kullanıcı adı zaten kullanımda.",
34 | },
35 | {
36 | code: "not_unique",
37 | field: "title",
38 | message: "Bu konu başlığı zaten mevcut..",
39 | },
40 | {
41 | code: "oauth2Error",
42 | message: "Bir hata oluştu. Lütfen daha sonra tekrar deneyin.",
43 | },
44 | {
45 | code: "already_oauth2_email",
46 | message: "Bu e-posta adresi ile zaten kayıt olunmuş. Lütfen giriş yapın.",
47 | },
48 | ];
49 |
50 | export default function (code: string | undefined, field?: string | undefined) {
51 | const error = errors.find((error) => error.code === code && error.field === field) || errors.find((error) => error.code === code);
52 |
53 | return error?.message;
54 | }
55 |
--------------------------------------------------------------------------------
/apps/web/src/utils/hooks.ts:
--------------------------------------------------------------------------------
1 | import { AppDispatch, RootState } from "@/store";
2 | import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
3 |
4 | export const useAppDispatch = () => useDispatch();
5 | export const useAppSelector: TypedUseSelectorHook = useSelector;
6 |
--------------------------------------------------------------------------------
/apps/web/src/utils/schemas.ts:
--------------------------------------------------------------------------------
1 | import * as Yup from "yup";
2 |
3 | export const LoginSchema = Yup.object().shape({
4 | email: Yup.string().email("Yazdığınız bir mail adresi değil.").required("Mail adresinizi girin."),
5 | password: Yup.string().min(5, "Şifreniz en az 5 karekter olması gerekmektedir.").required("Şifrenizi giriniz."),
6 | });
7 |
8 | export const RegisterSchema = Yup.object().shape({
9 | name: Yup.string().required("Adını yaz."),
10 | username: Yup.string().required("Kullanıcı adınızı yazınız."),
11 | email: Yup.string().email("Yazdığınız bir mail adresi değil.").required("Mail adresinizi girin."),
12 | password: Yup.string().min(5, "Şifreniz en az 5 karekter olması gerekmektedir.").required("Şifrenizi giriniz."),
13 | });
14 |
15 | export const CreateTopicSchema = Yup.object().shape({
16 | title: Yup.string().required("Konu başlığı yazmalısınız."),
17 | content: Yup.string().required("Konu içeriği yazmalısınız.").min(5, "Konu içeriği en az 5 karekterden oluşmalıdır."),
18 | });
19 |
20 | export const UpdateProfile = Yup.object().shape({
21 | username: Yup.string().required("Kullanıcı adınızı yazınız."),
22 | password: Yup.string().min(5, "Şifreniz en az 5 karekter olması gerekmektedir."),
23 | passwordConfirmation: Yup.string().oneOf([Yup.ref("password"), null], "Şifreler eşleşmiyor."),
24 | });
25 |
26 | export const AddEntrySchema = Yup.object().shape({
27 | content: Yup.string().required("Entry içeriği yazmalısınız.").min(5, "Entry içeriği en az 5 karekter olmalıdır."),
28 | });
29 |
--------------------------------------------------------------------------------
/apps/web/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require("@devsozluk/config/tailwind.config.js");
--------------------------------------------------------------------------------
/apps/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "baseUrl": ".",
23 | "paths": {
24 | "@/*": ["./src/*"]
25 | }
26 | },
27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
28 | "exclude": ["node_modules"]
29 | }
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev:web": "turbo run dev --filter=docs^... --filter=web --no-cache --continue",
5 | "dev:docs": "turbo run dev --filter=web^... --filter=docs --no-cache --continue",
6 | "dev": "turbo run dev --no-cache --continue",
7 | "build": "turbo run build",
8 | "lint": "turbo run lint",
9 | "clean": "turbo run clean && rm -rf node_modules",
10 | "format": "prettier --write \"**/*.{ts,tsx,md}\"",
11 | "changeset": "changeset",
12 | "version-packages": "changeset version",
13 | "release": "turbo run build --filter=docs^... && changeset publish"
14 | },
15 | "devDependencies": {
16 | "@changesets/cli": "^2.25.2",
17 | "eslint": "^8.29.0",
18 | "prettier": "^2.8.0",
19 | "turbo": "latest"
20 | },
21 | "packageManager": "pnpm@7.15.0"
22 | }
23 |
--------------------------------------------------------------------------------
/packages/config/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@devsozluk/config",
3 | "version": "0.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "files": [
7 | "eslint-preset.js",
8 | "tailwind.config.js"
9 | ],
10 | "dependencies": {
11 | "eslint-config-next": "latest",
12 | "eslint-config-prettier": "^8.3.0",
13 | "eslint-plugin-react": "7.29.4",
14 | "eslint-config-turbo": "latest"
15 | },
16 | "publishConfig": {
17 | "access": "public"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/config/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "../../packages/ui/src/**/*.tsx",
5 | "../../apps/web/src/**/*.{tsx,ts}",
6 | ],
7 | theme: {
8 | extend: {
9 | fontFamily: {
10 | poppins: "Poppins",
11 | },
12 | colors: {
13 | background: "#0f172a",
14 | placeholder: "#6F7181",
15 | primary: "#4fc6ff",
16 | secondary: "#f3f4f1",
17 | tertiary: "#909090",
18 | buttonPrimary: "#068cfc",
19 | },
20 | },
21 | },
22 | plugins: [],
23 | }
--------------------------------------------------------------------------------
/packages/eslint-config-devsozluk/index.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["next", "turbo", "prettier"],
3 | rules: {
4 | "react/display-name": [2, { "ignoreTranspilerName": true }],
5 | "@next/next/no-html-link-for-pages": "off",
6 | "react/jsx-key": "off",
7 | },
8 | };
--------------------------------------------------------------------------------
/packages/eslint-config-devsozluk/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eslint-config-devsozluk",
3 | "version": "0.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "dependencies": {
7 | "eslint-config-next": "latest",
8 | "eslint-config-prettier": "^8.3.0",
9 | "eslint-plugin-react": "7.29.4",
10 | "eslint-config-turbo": "latest"
11 | },
12 | "publishConfig": {
13 | "access": "public"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/tsconfig/base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Default",
4 | "compilerOptions": {
5 | "composite": false,
6 | "declaration": true,
7 | "declarationMap": true,
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "inlineSources": false,
11 | "isolatedModules": true,
12 | "moduleResolution": "node",
13 | "noUnusedLocals": false,
14 | "noUnusedParameters": false,
15 | "preserveWatchOutput": true,
16 | "skipLibCheck": true,
17 | "strict": true
18 | },
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/tsconfig/node16.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "Node 16",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "lib": ["ES2020"],
7 | "module": "commonjs",
8 | "target": "ES2020"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tsconfig/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@devsozluk/tsconfig",
3 | "version": "0.0.0",
4 | "private": true,
5 | "license": "MIT",
6 | "publishConfig": {
7 | "access": "public"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/tsconfig/react-library.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "display": "React Library",
4 | "extends": "./base.json",
5 | "compilerOptions": {
6 | "jsx": "react-jsx",
7 | "lib": ["dom", "ES2015"],
8 | "module": "ESNext",
9 | "target": "es6"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/ui/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ["devsozluk"],
4 | rules: {
5 | "react/display-name": "off"
6 | }
7 | };
8 |
--------------------------------------------------------------------------------
/packages/ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@devsozluk/ui",
3 | "version": "0.0.0",
4 | "main": "./dist/index.js",
5 | "module": "./dist/index.mjs",
6 | "types": "./dist/index.d.ts",
7 | "sideEffects": false,
8 | "license": "MIT",
9 | "files": [
10 | "dist/**"
11 | ],
12 | "scripts": {
13 | "build": "tsup src/index.tsx --format esm,cjs --dts --external react",
14 | "dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react",
15 | "lint": "TIMING=1 eslint \"src/**/*.ts*\"",
16 | "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
17 | },
18 | "devDependencies": {
19 | "@devsozluk/tsconfig": "workspace:*",
20 | "@types/react": "^18.0.9",
21 | "@types/react-dom": "^18.0.4",
22 | "eslint": "^8.15.0",
23 | "eslint-config-devsozluk": "workspace:*",
24 | "react": "^18.1.0",
25 | "tsup": "^5.10.1",
26 | "typescript": "^4.5.3"
27 | },
28 | "publishConfig": {
29 | "access": "public"
30 | },
31 | "dependencies": {
32 | "class-variance-authority": "^0.4.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/ui/src/Alert/Alert.icons.tsx:
--------------------------------------------------------------------------------
1 | const danger: React.FC = () => {
2 | return (
3 |
16 | );
17 | };
18 |
19 | const success: React.FC = () => {
20 | return (
21 |
34 | );
35 | };
36 |
37 | const warning: React.FC = () => {
38 | return (
39 |
52 | );
53 | };
54 |
55 | export default {
56 | danger,
57 | success,
58 | warning,
59 | };
60 |
--------------------------------------------------------------------------------
/packages/ui/src/Alert/Alert.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cva, VariantProps } from "class-variance-authority";
3 | import AlertIcons from "./Alert.icons";
4 |
5 | const alertStyles = cva("flex items-center justify-center gap-x-2 text-md ", {
6 | variants: {
7 | variant: {
8 | warning: "text-orange-500",
9 | danger: "text-red-500",
10 | success: "text-green-500",
11 | },
12 | },
13 | defaultVariants: {
14 | variant: "danger",
15 | },
16 | });
17 |
18 | type AlertBaseProps = VariantProps;
19 | export interface AlertProps extends AlertBaseProps {
20 | children?: React.ReactNode;
21 | }
22 |
23 | export const Alert: React.FC = ({ children, variant }) => {
24 | const AlertIcon = AlertIcons[variant as keyof typeof AlertIcons];
25 |
26 | return (
27 |
31 |
32 |
33 | {children}
34 |
35 |
36 | );
37 | };
38 |
--------------------------------------------------------------------------------
/packages/ui/src/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cva, VariantProps } from "class-variance-authority";
3 | import { Spinner } from "../Spinner/Spinner";
4 |
5 | const buttonStyles = cva(
6 | [
7 | "flex items-center justify-center gap-x-2 rounded border-2 text-lg font-medium transition-all disabled:opacity-50",
8 | ],
9 | {
10 | variants: {
11 | variant: {
12 | primary:
13 | "border-blue-700 bg-blue-700 text-white hover:bg-blue-800 hover:border-blue-800",
14 | outline:
15 | "border-blue-700 bg-transparent text-blue-700 hover:bg-blue-700 hover:text-white",
16 | danger:
17 | "border-red-500 bg-red-500 text-white hover:bg-transparent hover:text-red-500",
18 | },
19 | size: {
20 | sm: "py-2 px-4 text-sm",
21 | md: "py-2 px-6 text-md",
22 | lg: "py-3 px-8 text-lg",
23 | },
24 | },
25 | defaultVariants: {
26 | variant: "primary",
27 | size: "lg",
28 | },
29 | }
30 | );
31 |
32 | type ButtonBaseProps = VariantProps;
33 | export interface ButtonProps
34 | extends React.ButtonHTMLAttributes,
35 | ButtonBaseProps {
36 | renderLeftIcon?: React.ReactNode;
37 | renderRigthIcon?: React.ReactNode;
38 | loading?: boolean;
39 | }
40 |
41 | export const Button: React.FC = ({
42 | loading,
43 | children,
44 | variant,
45 | size,
46 | className,
47 | disabled,
48 | ...props
49 | }) => {
50 | return (
51 |
59 | );
60 | };
61 |
--------------------------------------------------------------------------------
/packages/ui/src/IconButton/IconButton.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Spinner } from "../Spinner/Spinner";
3 |
4 | export interface IconButtonProps
5 | extends React.ButtonHTMLAttributes {
6 | loading?: boolean;
7 | children: React.ReactNode;
8 | }
9 |
10 | export const IconButton: React.FC = ({
11 | children,
12 | loading,
13 | ...args
14 | }) => {
15 | return (
16 |
22 | );
23 | };
24 |
--------------------------------------------------------------------------------
/packages/ui/src/Input/Input.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cx } from "class-variance-authority";
3 |
4 | const baseClasses =
5 | "flex items-center space-x-3 rounded border border-placeholder py-4 px-3 text-placeholder transition-all focus-within:border-tertiary";
6 | const errorClasses = "!border-red-400 !text-red-400";
7 |
8 | export interface InputProps
9 | extends React.InputHTMLAttributes {
10 | label?: string;
11 | errorMessage?: string;
12 | renderLeftIcon?: React.ReactNode;
13 | renderRightIcon?: React.ReactNode;
14 | }
15 |
16 | const ErrorField = ({ errorMessage }: { errorMessage?: string }) => {
17 | return {errorMessage}
;
18 | };
19 |
20 | export const Input: React.FC = ({
21 | name,
22 | renderLeftIcon,
23 | label,
24 | errorMessage,
25 | className,
26 | ...props
27 | }) => {
28 | const InputClasses = cx(
29 | className,
30 | errorMessage ? cx(baseClasses, errorClasses) : baseClasses
31 | );
32 |
33 | return (
34 |
35 |
{label}
36 |
37 | {renderLeftIcon}
38 |
42 |
43 |
44 |
45 | );
46 | };
47 |
--------------------------------------------------------------------------------
/packages/ui/src/Spinner/Spinner.tsx:
--------------------------------------------------------------------------------
1 | import { cva, VariantProps } from "class-variance-authority";
2 |
3 | const spinnerStyles = cva(["animate-spin"], {
4 | variants: {
5 | variant: {
6 | light: "text-white",
7 | primary: "text-primary",
8 | },
9 | size: {
10 | sm: "h-4 w-4",
11 | md: "h-8 w-8",
12 | lg: "h-16 w-16",
13 | xl: "h-24 w-24",
14 | },
15 | },
16 | defaultVariants: {
17 | size: "sm",
18 | variant: "primary",
19 | },
20 | });
21 |
22 | type SpinnerBaseProps = VariantProps;
23 |
24 | export interface SpinnerProps extends SpinnerBaseProps {
25 | className?: string;
26 | }
27 |
28 | export const Spinner = ({ size, variant, className }: SpinnerProps) => {
29 | return (
30 |
51 | );
52 | };
53 |
--------------------------------------------------------------------------------
/packages/ui/src/TextArea/TextArea.tsx:
--------------------------------------------------------------------------------
1 | import { cx } from "class-variance-authority";
2 | import React from "react";
3 |
4 | const baseClasses =
5 | "w-full border border-gray-200 rounded bg-gray-50 dark:bg-gray-700 dark:border-gray-600";
6 | const errorClasses = "!border-red-400 !text-red-400";
7 |
8 | export interface TextAreaProps
9 | extends React.TextareaHTMLAttributes {
10 | label?: string;
11 | errorMessage?: string;
12 | renderLeftIcon?: React.ReactNode;
13 | renderRightIcon?: React.ReactNode;
14 | children?: React.ReactNode;
15 | }
16 |
17 | const ErrorField = ({ errorMessage }: { errorMessage?: string }) => {
18 | return {errorMessage}
;
19 | };
20 |
21 | export const TextArea = ({
22 | label,
23 | errorMessage,
24 | renderLeftIcon,
25 | renderRightIcon,
26 | children,
27 | ...props
28 | }: TextAreaProps) => {
29 | return (
30 |
31 |
{label}
32 |
35 |
36 |
40 |
41 | {children}
42 |
43 | {errorMessage &&
}
44 |
45 | );
46 | };
47 |
48 | const TextAreaActions = ({ children }: { children?: React.ReactNode }) => {
49 | return (
50 |
51 | {children}
52 |
53 | );
54 | };
55 |
56 | TextArea.Actions = TextAreaActions;
57 |
--------------------------------------------------------------------------------
/packages/ui/src/index.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | export { Button, type ButtonProps } from "./Button/Button";
3 | export { IconButton, type IconButtonProps } from "./IconButton/IconButton";
4 | export { Spinner, type SpinnerProps } from "./Spinner/Spinner";
5 | export { Input, type InputProps } from "./Input/Input";
6 | export { Alert, type AlertProps } from "./Alert/Alert";
7 | export { TextArea, type TextAreaProps } from "./TextArea/TextArea";
8 |
--------------------------------------------------------------------------------
/packages/ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@devsozluk/tsconfig/react-library.json",
3 | "include": ["."],
4 | "exclude": ["dist", "build", "node_modules"]
5 | }
6 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "apps/*"
3 | - "packages/*"
4 |
--------------------------------------------------------------------------------
/prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 2,
3 | "semi": true,
4 | "trailingComma": "all",
5 | "printWidth": 80,
6 | "arrowParens": "avoid",
7 | "singleQuote": true
8 | }
9 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 | "pipeline": {
4 | "build": {
5 | "outputs": [".next/**", "dist/**", "storybook-static/**"],
6 | "dependsOn": ["^build"],
7 | "env": ["NEXT_PUBLIC_ALTOGIC_ENV_URL", "NEXT_PUBLIC_ALTOGIC_CLIENT_KEY"]
8 | },
9 | "test": {
10 | "outputs": ["coverage/**"],
11 | "dependsOn": []
12 | },
13 | "lint": {},
14 | "dev": {
15 | "cache": false,
16 | "persistent": true
17 | },
18 | "clean": {
19 | "cache": false
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------