├── server ├── public │ ├── favicon.ico │ ├── robots.txt │ └── .htaccess ├── resources │ ├── css │ │ └── app.css │ ├── js │ │ ├── app.js │ │ └── bootstrap.js │ └── views │ │ └── vendor │ │ └── l5-swagger │ │ └── .gitkeep ├── database │ ├── .gitignore │ ├── seeders │ │ └── PermissionSeeder.php │ ├── factories │ │ ├── TagFactory.php │ │ ├── CommentFactory.php │ │ ├── UserFactory.php │ │ └── ArticleFactory.php │ └── migrations │ │ ├── 2024_11_08_200814_add_event_column_to_activity_log_table.php │ │ ├── 2021_05_23_150848_create_tags_table.php │ │ ├── 2024_11_08_200815_add_batch_uuid_column_to_activity_log_table.php │ │ ├── 2016_06_01_000005_create_oauth_personal_access_clients_table.php │ │ ├── 2014_10_12_100000_create_password_resets_table.php │ │ ├── 2016_06_01_000003_create_oauth_refresh_tokens_table.php │ │ ├── 2021_05_23_151511_create_article_tag_table.php │ │ ├── 2021_05_21_130644_add_fields_to_users_table.php │ │ ├── 2021_05_23_123208_create_user_follower_table.php │ │ ├── 2021_05_23_152011_create_article_favorite_table.php │ │ ├── 2016_06_01_000001_create_oauth_auth_codes_table.php │ │ ├── 2021_05_23_150839_create_comments_table.php │ │ ├── 2014_10_12_000000_create_users_table.php │ │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ │ ├── 2024_11_08_200813_create_activity_log_table.php │ │ ├── 2019_12_14_000001_create_personal_access_tokens_table.php │ │ ├── 2024_09_17_142234_alter_users_table_and_add_columns.php │ │ ├── 2016_06_01_000002_create_oauth_access_tokens_table.php │ │ ├── 2021_05_23_150828_create_articles_table.php │ │ └── 2016_06_01_000004_create_oauth_clients_table.php ├── storage │ ├── logs │ │ └── .gitignore │ ├── api-docs │ │ └── .gitignore │ ├── app │ │ ├── public │ │ │ └── .gitignore │ │ └── .gitignore │ ├── debugbar │ │ └── .gitignore │ └── framework │ │ ├── views │ │ └── .gitignore │ │ ├── cache │ │ ├── data │ │ │ └── .gitignore │ │ └── .gitignore │ │ ├── sessions │ │ └── .gitignore │ │ ├── testing │ │ └── .gitignore │ │ └── .gitignore ├── bootstrap │ └── cache │ │ └── .gitignore ├── config │ ├── frontend.php │ ├── jwt.php │ ├── cors.php │ ├── view.php │ └── services.php ├── TODO ├── phpstan.neon ├── .gitattributes ├── app │ ├── Exceptions │ │ └── JwtParseException.php │ ├── Contracts │ │ ├── JwtSubjectInterface.php │ │ ├── JwtValidatorInterface.php │ │ ├── JwtParserInterface.php │ │ └── JwtGeneratorInterface.php │ ├── Http │ │ ├── Controllers │ │ │ ├── Controller.php │ │ │ └── Api │ │ │ │ ├── TagsController.php │ │ │ │ └── UserController.php │ │ ├── Middleware │ │ │ ├── EncryptCookies.php │ │ │ ├── VerifyCsrfToken.php │ │ │ ├── PreventRequestsDuringMaintenance.php │ │ │ ├── TrustHosts.php │ │ │ ├── TrimStrings.php │ │ │ ├── Authenticate.php │ │ │ ├── TrustProxies.php │ │ │ └── RedirectIfAuthenticated.php │ │ ├── Resources │ │ │ └── Api │ │ │ │ ├── TagResource.php │ │ │ │ ├── CommentsCollection.php │ │ │ │ ├── BaseUserResource.php │ │ │ │ ├── ProfileResource.php │ │ │ │ ├── UserResource.php │ │ │ │ ├── TagsCollection.php │ │ │ │ ├── CommentResource.php │ │ │ │ └── ArticlesCollection.php │ │ └── Requests │ │ │ └── Api │ │ │ ├── FeedRequest.php │ │ │ ├── NewArticleRequest.php │ │ │ ├── NewCommentRequest.php │ │ │ ├── ArticleListRequest.php │ │ │ ├── LoginRequest.php │ │ │ └── UpdateArticleRequest.php │ ├── Providers │ │ ├── BroadcastServiceProvider.php │ │ ├── AppServiceProvider.php │ │ ├── EventServiceProvider.php │ │ └── AuthServiceProvider.php │ ├── Policies │ │ ├── CommentPolicy.php │ │ └── ArticlePolicy.php │ ├── Console │ │ └── Kernel.php │ └── Jwt │ │ ├── Validator.php │ │ └── Parser.php ├── bin │ └── prepare.sh ├── .gitignore ├── .editorconfig ├── tests │ ├── TestCase.php │ ├── CreatesApplication.php │ ├── Unit │ │ └── Jwt │ │ │ └── JwtParserTest.php │ └── Feature │ │ └── Api │ │ └── TagsTest.php ├── package.json ├── routes │ ├── web.php │ ├── channels.php │ └── console.php ├── webpack.mix.js ├── lang │ └── en │ │ ├── pagination.php │ │ ├── models.php │ │ ├── auth.php │ │ └── passwords.php ├── Makefile ├── LICENSE └── phpunit.xml ├── .gitignore ├── client ├── public │ ├── _redirects │ └── favicon.ico ├── .env.example ├── src │ ├── 6shared │ │ ├── lib │ │ │ ├── math │ │ │ │ ├── index.ts │ │ │ │ └── math.lib.ts │ │ │ ├── test │ │ │ │ ├── index.ts │ │ │ │ ├── example.test.ts │ │ │ │ ├── setup.ts │ │ │ │ └── test.lib.tsx │ │ │ ├── zustand │ │ │ │ ├── index.ts │ │ │ │ └── zustand.lib.ts │ │ │ ├── react-query │ │ │ │ ├── index.ts │ │ │ │ └── queryClient.ts │ │ │ ├── react-hook-form │ │ │ │ ├── index.ts │ │ │ │ └── react-hook-form.lib.ts │ │ │ ├── image │ │ │ │ ├── index.ts │ │ │ │ └── aspect-ratio-validator.ts │ │ │ ├── react │ │ │ │ ├── index.ts │ │ │ │ ├── compose.ts │ │ │ │ └── react.hoc.tsx │ │ │ ├── axios │ │ │ │ ├── index.ts │ │ │ │ ├── AxiosLib.ts │ │ │ │ ├── AxiosValidationError.ts │ │ │ │ └── AxiosContracts.ts │ │ │ └── react-router │ │ │ │ ├── index.ts │ │ │ │ ├── react-router.types.ts │ │ │ │ ├── react-router.contracts.ts │ │ │ │ └── config.ts │ │ ├── ui │ │ │ ├── button │ │ │ │ ├── index.ts │ │ │ │ └── button.ui.tsx │ │ │ ├── stack │ │ │ │ ├── index.ts │ │ │ │ ├── stack.ui.tsx │ │ │ │ └── stack.module.css │ │ │ ├── skeleton │ │ │ │ ├── index.ts │ │ │ │ ├── skeleton.ui.tsx │ │ │ │ └── skeleton.module.css │ │ │ ├── error-list │ │ │ │ ├── index.ts │ │ │ │ └── error-list.ui.tsx │ │ │ ├── button-google │ │ │ │ └── index.ts │ │ │ ├── tabs │ │ │ │ ├── index.ts │ │ │ │ └── tabs.model.ts │ │ │ ├── spinner │ │ │ │ ├── index.ts │ │ │ │ ├── spinner.ui.tsx │ │ │ │ └── spinner.model.ts │ │ │ ├── error-handler │ │ │ │ ├── index.ts │ │ │ │ ├── error-handler.lib.ts │ │ │ │ └── error-handler.ui.tsx │ │ │ └── dropdown-menu │ │ │ │ └── index.ts │ │ ├── api │ │ │ ├── tag │ │ │ │ ├── index.ts │ │ │ │ ├── tag.contracts.ts │ │ │ │ ├── tag.types.ts │ │ │ │ └── tag.service.ts │ │ │ ├── auth │ │ │ │ ├── index.ts │ │ │ │ └── auth.types.ts │ │ │ ├── profile │ │ │ │ ├── profile.types.ts │ │ │ │ ├── index.ts │ │ │ │ ├── profile.contracts.ts │ │ │ │ └── profile.service.ts │ │ │ ├── article │ │ │ │ ├── index.ts │ │ │ │ └── article.types.ts │ │ │ ├── comment │ │ │ │ ├── index.ts │ │ │ │ ├── comment.types.ts │ │ │ │ ├── comment.contracts.ts │ │ │ │ └── comment.service.ts │ │ │ ├── favorite │ │ │ │ ├── index.ts │ │ │ │ ├── favorite.types.ts │ │ │ │ ├── favorite.contracts.ts │ │ │ │ └── favorite.service.ts │ │ │ └── index.ts │ │ └── session │ │ │ ├── index.ts │ │ │ ├── session.contracts.ts │ │ │ ├── session.lib.ts │ │ │ ├── session.queries.ts │ │ │ └── session.model.ts │ ├── 5entities │ │ ├── tag │ │ │ ├── index.ts │ │ │ ├── tag.contracts.ts │ │ │ ├── tag.types.ts │ │ │ ├── tag.lib.ts │ │ │ └── tag.queries.ts │ │ ├── profile │ │ │ ├── index.ts │ │ │ ├── profile.types.ts │ │ │ ├── profile.contracts.ts │ │ │ ├── profile.lib.ts │ │ │ └── profile.queries.ts │ │ ├── comment │ │ │ ├── index.ts │ │ │ ├── comment.types.ts │ │ │ ├── comment.contracts.ts │ │ │ ├── comment.lib.ts │ │ │ └── comment.queries.ts │ │ └── article │ │ │ ├── index.ts │ │ │ ├── article.types.ts │ │ │ ├── article.lib.ts │ │ │ └── article.contracts.ts │ ├── 2pages │ │ ├── home │ │ │ ├── index.ts │ │ │ └── home-page.route.tsx │ │ ├── page-404 │ │ │ ├── index.ts │ │ │ ├── page-404.module.css │ │ │ ├── page-404.ui.tsx │ │ │ └── page-404.route.tsx │ │ ├── editor │ │ │ ├── index.ts │ │ │ ├── editor-page.ui.tsx │ │ │ ├── editor-page.skeleton.tsx │ │ │ └── editor-page.route.tsx │ │ ├── article │ │ │ ├── index.ts │ │ │ ├── articles-page.css │ │ │ └── article-page.route.tsx │ │ ├── profile │ │ │ ├── index.ts │ │ │ └── profile-page.route.tsx │ │ ├── settings │ │ │ ├── index.ts │ │ │ ├── settings-page.model.ts │ │ │ ├── settings-page.ui.tsx │ │ │ ├── settings-page.route.tsx │ │ │ └── settings-page.skeleton.tsx │ │ └── layouts │ │ │ ├── index.ts │ │ │ ├── generic.layout.tsx │ │ │ └── guest.layout.tsx │ ├── 4features │ │ ├── session │ │ │ ├── logout │ │ │ │ ├── index.ts │ │ │ │ ├── logout.ui.tsx │ │ │ │ ├── logout.mutation.ts │ │ │ │ └── logout.test.tsx │ │ │ ├── index.ts │ │ │ ├── update-session │ │ │ │ ├── index.ts │ │ │ │ └── update-session.skeleton.tsx │ │ │ └── login │ │ │ │ └── login.mutation.ts │ │ ├── article │ │ │ ├── create-article │ │ │ │ ├── index.ts │ │ │ │ ├── create-article.contract.ts │ │ │ │ └── create-article.lib.ts │ │ │ ├── update-article │ │ │ │ ├── index.ts │ │ │ │ ├── update-article.contract.ts │ │ │ │ ├── update-article.skeleton.tsx │ │ │ │ └── update-article.lib.ts │ │ │ ├── delete-article │ │ │ │ ├── index.ts │ │ │ │ ├── delete-article.ui.tsx │ │ │ │ └── delete-article.mutation.ts │ │ │ ├── filter-article │ │ │ │ ├── index.ts │ │ │ │ └── filter-article.skeleton.tsx │ │ │ ├── favorite-article │ │ │ │ └── index.ts │ │ │ ├── unfavorite-article │ │ │ │ └── index.ts │ │ │ └── index.ts │ │ ├── comment │ │ │ ├── create-comment │ │ │ │ ├── index.ts │ │ │ │ ├── create-comment.lib.ts │ │ │ │ └── create-comment.skeleton.tsx │ │ │ ├── index.ts │ │ │ └── delete-comment │ │ │ │ ├── index.ts │ │ │ │ └── delete-comment.ui.tsx │ │ └── profile │ │ │ ├── follow-profile │ │ │ ├── index.ts │ │ │ └── follow-profile.ui.tsx │ │ │ ├── index.ts │ │ │ └── unfollow-profile │ │ │ ├── index.ts │ │ │ └── unfollow-profile.ui.tsx │ ├── 3widgets │ │ ├── comments-feed │ │ │ ├── index.ts │ │ │ └── comments-feed.skeleton.tsx │ │ └── articles-feed │ │ │ ├── index.ts │ │ │ └── articles-feed.css │ ├── vite-env.d.ts │ └── 1app │ │ ├── auth.tsx │ │ ├── providers │ │ ├── index.tsx │ │ └── QueryClientProvider.tsx │ │ └── index.tsx ├── logo.gif ├── tsconfig.node.json ├── prettier.config.cjs ├── index.html ├── LICENSE └── tsconfig.json ├── docs ├── Como as ferramentas de execução do Mural Funciona.md ├── imagens │ ├── 401.png │ └── devtools_network.png ├── fundamentos_react.md ├── docker │ ├── README.md │ ├── nginx │ │ └── nginx-docker.conf │ └── Dockerfile ├── OVERVIEW.md ├── outras_ferramentas.md ├── problemas_comuns.md └── docker.md ├── .vscode ├── extensions.json └── settings.json ├── playwright ├── .gitignore ├── tests │ ├── utils │ │ ├── index.js │ │ └── selectArticlesForPage.js │ └── home-page │ │ └── homeTitleMatchesArticle.spec.js ├── example.spec.js ├── package.json ├── .env.example └── utils │ └── global-teardown.js └── .github ├── ISSUE_TEMPLATE ├── feature_request.md └── bug_report.md └── PULL_REQUEST_TEMPLATE.md /server/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/resources/css/app.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode -------------------------------------------------------------------------------- /server/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /client/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /server/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | -------------------------------------------------------------------------------- /server/resources/views/vendor/l5-swagger/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /docs/Como as ferramentas de execução do Mural Funciona.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /server/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /server/storage/api-docs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /server/storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /server/storage/debugbar/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /server/storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /client/.env.example: -------------------------------------------------------------------------------- 1 | VITE_API_BASE_URL='http://localhost:8081/api' -------------------------------------------------------------------------------- /client/src/6shared/lib/math/index.ts: -------------------------------------------------------------------------------- 1 | export * from './math.lib' 2 | -------------------------------------------------------------------------------- /client/src/6shared/lib/test/index.ts: -------------------------------------------------------------------------------- 1 | export * from './test.lib' 2 | -------------------------------------------------------------------------------- /server/storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /server/storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /server/storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /server/storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /client/src/6shared/lib/zustand/index.ts: -------------------------------------------------------------------------------- 1 | export * from './zustand.lib' 2 | -------------------------------------------------------------------------------- /client/src/6shared/ui/button/index.ts: -------------------------------------------------------------------------------- 1 | export { Button } from './button.ui' -------------------------------------------------------------------------------- /client/src/6shared/ui/stack/index.ts: -------------------------------------------------------------------------------- 1 | export { Stack } from './stack.ui' 2 | -------------------------------------------------------------------------------- /server/storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /client/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irmaos-dev/mural/HEAD/client/logo.gif -------------------------------------------------------------------------------- /client/src/5entities/tag/index.ts: -------------------------------------------------------------------------------- 1 | export { TagQueries } from './tag.queries' 2 | -------------------------------------------------------------------------------- /client/src/2pages/home/index.ts: -------------------------------------------------------------------------------- 1 | export { homePageRoute } from './home-page.route' 2 | -------------------------------------------------------------------------------- /client/src/2pages/page-404/index.ts: -------------------------------------------------------------------------------- 1 | export { page404Route } from './page-404.route' 2 | -------------------------------------------------------------------------------- /client/src/6shared/ui/skeleton/index.ts: -------------------------------------------------------------------------------- 1 | export { Skeleton } from './skeleton.ui' 2 | -------------------------------------------------------------------------------- /client/src/2pages/editor/index.ts: -------------------------------------------------------------------------------- 1 | export { editorPageRoute } from './editor-page.route' 2 | -------------------------------------------------------------------------------- /client/src/4features/session/logout/index.ts: -------------------------------------------------------------------------------- 1 | export { LogoutButton } from './logout.ui' 2 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-query/index.ts: -------------------------------------------------------------------------------- 1 | export { queryClient } from './queryClient' 2 | -------------------------------------------------------------------------------- /client/src/6shared/ui/error-list/index.ts: -------------------------------------------------------------------------------- 1 | export { ErrorList } from './error-list.ui' 2 | -------------------------------------------------------------------------------- /docs/imagens/401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irmaos-dev/mural/HEAD/docs/imagens/401.png -------------------------------------------------------------------------------- /client/src/2pages/article/index.ts: -------------------------------------------------------------------------------- 1 | export { articlePageRoute } from './article-page.route' 2 | -------------------------------------------------------------------------------- /client/src/2pages/profile/index.ts: -------------------------------------------------------------------------------- 1 | export { profilePageRoute } from './profile-page.route' 2 | -------------------------------------------------------------------------------- /client/src/2pages/settings/index.ts: -------------------------------------------------------------------------------- 1 | export { settingsPageRoute } from './settings-page.route' 2 | -------------------------------------------------------------------------------- /client/src/3widgets/comments-feed/index.ts: -------------------------------------------------------------------------------- 1 | export { CommentsFeed } from './comments-feed.ui' 2 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irmaos-dev/mural/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/4features/session/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logout' 2 | export * from './update-session' 3 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-hook-form/index.ts: -------------------------------------------------------------------------------- 1 | export { hasMessages } from './react-hook-form.lib' 2 | -------------------------------------------------------------------------------- /client/src/6shared/ui/button-google/index.ts: -------------------------------------------------------------------------------- 1 | export { GoogleButton } from './button.google.ui.jsx' 2 | -------------------------------------------------------------------------------- /client/src/6shared/lib/image/index.ts: -------------------------------------------------------------------------------- 1 | export { ImageAspectRatioValidator } from './aspect-ratio-validator' -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "open-southeners.laravel-pint" 4 | ] 5 | } -------------------------------------------------------------------------------- /client/src/4features/article/create-article/index.ts: -------------------------------------------------------------------------------- 1 | export { CreateArticleForm } from './create-article.ui' 2 | -------------------------------------------------------------------------------- /client/src/4features/article/update-article/index.ts: -------------------------------------------------------------------------------- 1 | export { UpdateArticleForm } from './update-article.ui' 2 | -------------------------------------------------------------------------------- /client/src/4features/comment/create-comment/index.ts: -------------------------------------------------------------------------------- 1 | export { CreateCommentForm } from './create-comment.ui' 2 | -------------------------------------------------------------------------------- /client/src/4features/comment/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-comment' 2 | export * from './delete-comment' 3 | -------------------------------------------------------------------------------- /client/src/4features/profile/follow-profile/index.ts: -------------------------------------------------------------------------------- 1 | export { FollowUserButton } from './follow-profile.ui' 2 | -------------------------------------------------------------------------------- /client/src/4features/profile/index.ts: -------------------------------------------------------------------------------- 1 | export * from './follow-profile' 2 | export * from './unfollow-profile' 3 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react/index.ts: -------------------------------------------------------------------------------- 1 | export * from './react.hoc' 2 | export { compose } from './compose' 3 | -------------------------------------------------------------------------------- /client/src/4features/article/delete-article/index.ts: -------------------------------------------------------------------------------- 1 | export { DeleteArticleButton } from './delete-article.ui' 2 | -------------------------------------------------------------------------------- /client/src/4features/comment/delete-comment/index.ts: -------------------------------------------------------------------------------- 1 | export { DeleteCommentButtton } from './delete-comment.ui' 2 | -------------------------------------------------------------------------------- /client/src/4features/profile/unfollow-profile/index.ts: -------------------------------------------------------------------------------- 1 | export { UnfollowUserButton } from './unfollow-profile.ui' 2 | -------------------------------------------------------------------------------- /client/src/6shared/ui/tabs/index.ts: -------------------------------------------------------------------------------- 1 | export { Tabs } from './tabs.ui' 2 | export * as tabsModel from './tabs.model' 3 | -------------------------------------------------------------------------------- /docs/imagens/devtools_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/irmaos-dev/mural/HEAD/docs/imagens/devtools_network.png -------------------------------------------------------------------------------- /client/src/2pages/article/articles-page.css: -------------------------------------------------------------------------------- 1 | .article-img-feed { 2 | aspect-ratio: 16 / 9; 3 | max-width: 100%; 4 | } -------------------------------------------------------------------------------- /server/config/frontend.php: -------------------------------------------------------------------------------- 1 | env("FRONT_END_URL"), ]; 6 | -------------------------------------------------------------------------------- /client/src/5entities/tag/tag.contracts.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const TagsSchema = z.array(z.string()) 4 | -------------------------------------------------------------------------------- /playwright/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | /test-results/ 3 | /playwright-report/ 4 | /blob-report/ 5 | /playwright/.cache/ 6 | .env -------------------------------------------------------------------------------- /client/src/6shared/ui/spinner/index.ts: -------------------------------------------------------------------------------- 1 | export { Spinner } from './spinner.ui' 2 | export * as spinnerModel from './spinner.model' 3 | -------------------------------------------------------------------------------- /server/TODO: -------------------------------------------------------------------------------- 1 | Article vem sem o campo favorited quando não está logado 2 | 3 | validação UpdateUserRequest está lendo campo inexistente -------------------------------------------------------------------------------- /client/src/6shared/ui/error-handler/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error-handler.lib' 2 | export { ErrorHandler } from './error-handler.ui' 3 | -------------------------------------------------------------------------------- /client/src/5entities/profile/index.ts: -------------------------------------------------------------------------------- 1 | export * as profileTypes from './profile.types' 2 | export { ProfileQueries } from './profile.queries' 3 | -------------------------------------------------------------------------------- /client/src/4features/article/filter-article/index.ts: -------------------------------------------------------------------------------- 1 | export * from './filter-article.ui' 2 | export * as filterArticleModel from './filter-article.model' 3 | -------------------------------------------------------------------------------- /playwright/tests/utils/index.js: -------------------------------------------------------------------------------- 1 | const { selectArticlesForPage } = require("./selectArticlesForPage"); 2 | 3 | module.exports = { selectArticlesForPage }; 4 | -------------------------------------------------------------------------------- /client/src/3widgets/articles-feed/index.ts: -------------------------------------------------------------------------------- 1 | export { ArticlesFeed } from './articles-feed.ui' 2 | export { ArticlesFeedSkeleton } from './articles-feed.skeleton' 3 | -------------------------------------------------------------------------------- /client/src/4features/article/favorite-article/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | FavoriteArticleBriefButton, 3 | FavoriteArticleExtendedButton, 4 | } from './favorite-article.ui' 5 | -------------------------------------------------------------------------------- /client/src/5entities/tag/tag.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { TagsSchema } from './tag.contracts' 3 | 4 | export type Tags = z.infer 5 | -------------------------------------------------------------------------------- /client/src/2pages/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export { GenericLayout } from './generic.layout' 2 | export { GuestLayout } from './guest.layout' 3 | export { UserLayout } from './user.layout' 4 | -------------------------------------------------------------------------------- /client/src/4features/article/unfavorite-article/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | UnfavoriteArticleBriefButton, 3 | UnfavoriteArticleExtendedButton, 4 | } from './unfavorite-article.ui' 5 | -------------------------------------------------------------------------------- /server/phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | 4 | parameters: 5 | level: 4 6 | reportUnmatchedIgnoredErrors: false 7 | paths: 8 | - app 9 | -------------------------------------------------------------------------------- /client/src/3widgets/articles-feed/articles-feed.css: -------------------------------------------------------------------------------- 1 | .article-img-feed { 2 | aspect-ratio: 16 / 9; 3 | width: 100%; 4 | margin-bottom: 20px; 5 | margin-top: 30px; 6 | 7 | } -------------------------------------------------------------------------------- /client/src/4features/session/update-session/index.ts: -------------------------------------------------------------------------------- 1 | export { UpdateSessionForm } from './update-session.ui' 2 | export { UpdateSessionFormSkeleton } from './update-session.skeleton' 3 | -------------------------------------------------------------------------------- /client/src/6shared/api/tag/index.ts: -------------------------------------------------------------------------------- 1 | export * as tagContractsDto from './tag.contracts' 2 | export { TagService } from './tag.service' 3 | export * as tagTypesDto from './tag.types' 4 | -------------------------------------------------------------------------------- /client/src/5entities/profile/profile.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ProfileSchema } from './profile.contracts' 3 | 4 | export type Profile = z.infer 5 | -------------------------------------------------------------------------------- /client/src/6shared/api/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * as authContractsDto from './auth.contracts' 2 | export { AuthService } from './auth.service' 3 | export * as authTypesDto from './auth.types' 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "laravel-pint.enable": true, 3 | "[php]": { 4 | "editor.defaultFormatter": "open-southeners.laravel-pint" 5 | }, 6 | "php.version": "8.3" 7 | } -------------------------------------------------------------------------------- /client/src/6shared/api/profile/profile.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { ProfileDtoSchema } from './profile.contracts' 3 | 4 | export type ProfileDto = z.infer 5 | -------------------------------------------------------------------------------- /client/src/6shared/lib/axios/index.ts: -------------------------------------------------------------------------------- 1 | export { AxiosContracts } from './AxiosContracts' 2 | export { AxiosLib } from './AxiosLib' 3 | export { AxiosValidationError } from './AxiosValidationError' 4 | -------------------------------------------------------------------------------- /server/storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /client/src/5entities/comment/index.ts: -------------------------------------------------------------------------------- 1 | export * as commentContracts from './comment.contracts' 2 | export * as commentTypes from './comment.types' 3 | export { CommentQueries } from './comment.queries' 4 | -------------------------------------------------------------------------------- /client/src/6shared/api/tag/tag.contracts.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const TagDtoSchema = z.string() 4 | export const TagsDtoSchema = z.object({ 5 | tags: z.array(TagDtoSchema), 6 | }) 7 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-router/index.ts: -------------------------------------------------------------------------------- 1 | export { pathKeys } from './config' 2 | export * as routerContracts from './react-router.contracts' 3 | export * as routerTypes from './react-router.types' 4 | -------------------------------------------------------------------------------- /client/src/6shared/api/article/index.ts: -------------------------------------------------------------------------------- 1 | export * as articleContractsDto from './article.contracts' 2 | export { ArticleService } from './article.service' 3 | export * as articleTypesDto from './article.types' 4 | -------------------------------------------------------------------------------- /client/src/6shared/api/comment/index.ts: -------------------------------------------------------------------------------- 1 | export * as commentContractsDto from './comment.contracts' 2 | export { CommentService } from './comment.service' 3 | export * as commentTypesDto from './comment.types' 4 | -------------------------------------------------------------------------------- /client/src/6shared/api/profile/index.ts: -------------------------------------------------------------------------------- 1 | export * as profileContractsDto from './profile.contracts' 2 | export { ProfileService } from './profile.service' 3 | export * as profileTypesDto from './profile.types' 4 | -------------------------------------------------------------------------------- /client/src/6shared/ui/dropdown-menu/index.ts: -------------------------------------------------------------------------------- 1 | export { DropdownMenu } from './dropdown.ui' 2 | export { createDropdownMenuStore } from './dropdown.model' 3 | export type { DropdownMenuStore } from './dropdown.model' 4 | -------------------------------------------------------------------------------- /client/src/6shared/api/favorite/index.ts: -------------------------------------------------------------------------------- 1 | export * as favoriteContractsDto from './favorite.contracts' 2 | export { FavoriteService } from './favorite.service' 3 | export * as favoriteTypesDto from './favorite.types' 4 | -------------------------------------------------------------------------------- /client/src/6shared/api/favorite/favorite.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { FavoriteArticleDtoSchema } from './favorite.contracts' 3 | 4 | export type FavoriteArticleDto = z.infer 5 | -------------------------------------------------------------------------------- /server/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.blade.php diff=html 4 | *.css diff=css 5 | *.html diff=html 6 | *.md diff=markdown 7 | *.php diff=php 8 | 9 | /.github export-ignore 10 | CHANGELOG.md export-ignore 11 | -------------------------------------------------------------------------------- /server/app/Exceptions/JwtParseException.php: -------------------------------------------------------------------------------- 1 | 5 | export type TagsDto = z.infer 6 | -------------------------------------------------------------------------------- /client/src/5entities/article/index.ts: -------------------------------------------------------------------------------- 1 | export * as articleContracts from './article.contracts' 2 | export * as articleLib from './article.lib' 3 | export { ArticleQueries } from './article.queries' 4 | export * as articleTypes from './article.types' 5 | -------------------------------------------------------------------------------- /server/bin/prepare.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | docker run --rm \ 4 | -u "$(id -u):$(id -g)" \ 5 | -v "$(pwd):/var/www/html" \ 6 | -w /var/www/html \ 7 | laravelsail/php83-composer:latest \ 8 | composer install --ignore-platform-reqs 9 | -------------------------------------------------------------------------------- /client/src/4features/article/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-article' 2 | export * from './update-article' 3 | export * from './delete-article' 4 | export * from './favorite-article' 5 | export * from './unfavorite-article' 6 | export * from './filter-article' 7 | -------------------------------------------------------------------------------- /client/src/5entities/comment/comment.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { CommentSchema, CommentsSchema } from './comment.contracts' 3 | 4 | export type Comment = z.infer 5 | export type Comments = z.infer 6 | -------------------------------------------------------------------------------- /client/src/6shared/session/index.ts: -------------------------------------------------------------------------------- 1 | export * as sessionLib from './session.lib' 2 | export { useSessionStore } from './session.model' 3 | export * from './session.queries' 4 | export * from './session.service' 5 | export * as sessionTypes from './session.types' 6 | -------------------------------------------------------------------------------- /client/src/6shared/lib/math/math.lib.ts: -------------------------------------------------------------------------------- 1 | export function getRandomNumber(config: { min: number; max: number }) { 2 | const min = Math.ceil(config.min) 3 | const max = Math.floor(config.max) 4 | 5 | return Math.floor(Math.random() * (max - min + 1)) + min 6 | } 7 | -------------------------------------------------------------------------------- /client/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /client/src/6shared/session/session.contracts.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const SessionSchema = z.object({ 4 | email: z.string(), 5 | token: z.string(), 6 | username: z.string(), 7 | bio: z.string(), 8 | image: z.string(), 9 | perms: z.array(z.string()), 10 | }) 11 | -------------------------------------------------------------------------------- /client/src/6shared/api/profile/profile.contracts.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const ProfileDtoSchema = z.object({ 4 | profile: z.object({ 5 | username: z.string(), 6 | bio: z.nullable(z.string()), 7 | image: z.string(), 8 | following: z.boolean(), 9 | }), 10 | }) 11 | -------------------------------------------------------------------------------- /client/src/6shared/lib/test/example.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | 3 | describe('example test', () => { 4 | it('should be truthy', () => { 5 | expect(true).toBeTruthy() 6 | }) 7 | 8 | it('should be falsy', () => { 9 | expect(false).toBeFalsy() 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /client/src/2pages/layouts/generic.layout.tsx: -------------------------------------------------------------------------------- 1 | import { useSessionStore } from '~6shared/session' 2 | import { GuestLayout } from './guest.layout' 3 | import { UserLayout } from './user.layout' 4 | 5 | export function GenericLayout() { 6 | return useSessionStore.use.session() ? : 7 | } 8 | -------------------------------------------------------------------------------- /playwright/example.spec.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const { test, expect } = require('@playwright/test'); 3 | 4 | test('has title', async ({ page }) => { 5 | await page.goto("/", { timeout: 60000, waitUntil: 'load' }); 6 | 7 | // Expect a title "to contain" a substring. 8 | await expect(page).toHaveTitle(/Conduit/); 9 | }); 10 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-query/queryClient.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/react-query' 2 | 3 | export const queryClient = new QueryClient({ 4 | defaultOptions: { 5 | queries: { 6 | retry: false, 7 | refetchOnWindowFocus: false, 8 | staleTime: 1000 * 60 * 3, 9 | }, 10 | }, 11 | }) 12 | -------------------------------------------------------------------------------- /client/src/2pages/page-404/page-404.module.css: -------------------------------------------------------------------------------- 1 | .outer-wrapper { 2 | position: absolute; 3 | inset: 0; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | 9 | .inner-wrapper { 10 | width: 100%; 11 | padding: 2rem; 12 | color: #fff; 13 | text-align: center; 14 | background: #333; 15 | } 16 | -------------------------------------------------------------------------------- /server/app/Contracts/JwtSubjectInterface.php: -------------------------------------------------------------------------------- 1 | 2 | /// 3 | /// 4 | 5 | import '@tanstack/react-query' 6 | import { AxiosError } from 'axios' 7 | 8 | declare module '@tanstack/react-query' { 9 | interface Register { 10 | defaultError: AxiosError 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /server/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | (data: D): AxiosResponse { 5 | return { 6 | data, 7 | status: 200, 8 | statusText: 'OK', 9 | headers: {}, 10 | config: { headers: new AxiosHeaders() }, 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/5entities/profile/profile.lib.ts: -------------------------------------------------------------------------------- 1 | import { profileTypesDto } from '~6shared/api/profile' 2 | import { Profile } from './profile.types' 3 | 4 | export function transformProfileDtoToProfile( 5 | profileDto: profileTypesDto.ProfileDto, 6 | ): Profile { 7 | const { profile } = profileDto 8 | 9 | return { 10 | ...profile, 11 | bio: profile.bio || '', 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react/compose.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from 'react' 2 | 3 | export function compose( 4 | ...hocs: Array<(component: ComponentType) => ComponentType> 5 | ): (component: ComponentType) => ComponentType { 6 | return (component: ComponentType) => 7 | hocs.reduceRight((wrapped, hoc) => hoc(wrapped), component) 8 | } 9 | -------------------------------------------------------------------------------- /docs/fundamentos_react.md: -------------------------------------------------------------------------------- 1 | tanstack query 2 | zustand 3 | hooks customizados 4 | suspense / lazy 5 | cn 6 | router 7 | testes 8 | 9 | 10 | 11 | Query 12 | https://reactpractice.dev/exercise/build-a-simple-shopping-cart-using-react-query/ 13 | structuring the component tree 14 | sharing state across components 15 | combining server state with local state 16 | working with derived state 17 | 18 | -------------------------------------------------------------------------------- /client/src/6shared/api/comment/comment.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { 3 | CommentDtoSchema, 4 | CommentsDtoSchema, 5 | CreateCommentDtoSchema, 6 | } from './comment.contracts' 7 | 8 | export type CommentDto = z.infer 9 | export type CommentsDto = z.infer 10 | export type CreateCommentDto = z.infer 11 | -------------------------------------------------------------------------------- /server/config/jwt.php: -------------------------------------------------------------------------------- 1 | (int) env('JWT_EXPIRATION', 3600), // one hour 11 | 12 | /** 13 | * Default JWT headers. 14 | */ 15 | 'headers' => [ 16 | 'alg' => 'HS256', 17 | 'typ' => 'JWT', 18 | ], 19 | 20 | ]; 21 | -------------------------------------------------------------------------------- /client/src/6shared/api/tag/tag.service.ts: -------------------------------------------------------------------------------- 1 | import { AxiosContracts } from '../../lib/axios' 2 | import { realworld } from '../index' 3 | import { TagsDtoSchema } from './tag.contracts' 4 | 5 | export class TagService { 6 | static tagsQuery(config: { signal?: AbortSignal }) { 7 | return realworld 8 | .get('/tags', config) 9 | .then(AxiosContracts.responseContract(TagsDtoSchema)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/app/Contracts/JwtValidatorInterface.php: -------------------------------------------------------------------------------- 1 | 10 | {Object.values(errors).map((error) => ( 11 |
  • {error.message}
  • 12 | ))} 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /client/src/6shared/ui/error-handler/error-handler.lib.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const isDevelopment = import.meta.env.DEV 3 | 4 | export function logError( 5 | error: Error, 6 | info: { componentStack?: string | null }, 7 | ) { 8 | if (!isDevelopment) { 9 | // Log error to an external service in production 10 | } else { 11 | console.log('Caught error:', error) 12 | console.log('Error details:', info) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-hook-form/react-hook-form.lib.ts: -------------------------------------------------------------------------------- 1 | interface FieldError { 2 | message: string 3 | } 4 | 5 | export function hasMessages(errors: any): errors is Record { 6 | return Object.values(errors).some( 7 | (error) => 8 | error !== undefined && 9 | error !== null && 10 | typeof error === 'object' && 11 | 'message' in error && 12 | typeof error.message === 'string', 13 | ) 14 | } 15 | -------------------------------------------------------------------------------- /server/app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $except = [ 17 | 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /client/src/2pages/settings/settings-page.model.ts: -------------------------------------------------------------------------------- 1 | import { LoaderFunctionArgs, redirect } from 'react-router-dom' 2 | import { pathKeys } from '~6shared/lib/react-router' 3 | import { useSessionStore } from '~6shared/session' 4 | 5 | export class SettingsLoader { 6 | static async settingsPage(args: LoaderFunctionArgs) { 7 | if (useSessionStore.getState().session) { 8 | return args 9 | } 10 | return redirect(pathKeys.login()) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $except = [ 17 | 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /client/src/6shared/api/auth/auth.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { 3 | CreateUserDtoSchema, 4 | LoginUserDtoSchema, 5 | UpdateUserDtoSchema, 6 | UserDtoSchema, 7 | } from './auth.contracts' 8 | 9 | export type UserDto = z.infer 10 | export type UpdateUserDto = z.infer 11 | export type CreateUserDto = z.infer 12 | export type LoginUserDto = z.infer 13 | -------------------------------------------------------------------------------- /client/src/5entities/comment/comment.contracts.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const CommentSchema = z.object({ 4 | id: z.number(), 5 | createdAt: z.coerce.date(), 6 | updatedAt: z.coerce.date(), 7 | body: z.string(), 8 | author: z.object({ 9 | username: z.string(), 10 | bio: z.nullable(z.string()), 11 | image: z.string(), 12 | following: z.boolean(), 13 | }), 14 | }) 15 | 16 | export const CommentsSchema = z.map(z.number(), CommentSchema) 17 | -------------------------------------------------------------------------------- /client/src/6shared/session/session.lib.ts: -------------------------------------------------------------------------------- 1 | import { authTypesDto } from '~6shared/api/auth' 2 | import { Session } from './session.types' 3 | 4 | export function transformUserDtoToSession( 5 | userDto: authTypesDto.UserDto, 6 | ): Session { 7 | const { user } = userDto 8 | return { 9 | email: user.email, 10 | image: user.image, 11 | token: user.token, 12 | username: user.username, 13 | bio: user?.bio || '', 14 | perms: user.perms, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/5entities/article/article.types.ts: -------------------------------------------------------------------------------- 1 | import { InfiniteData } from '@tanstack/react-query' 2 | import { z } from 'zod' 3 | import { 4 | ArticleSchema, 5 | ArticlesSchema, 6 | FilterQuerySchema, 7 | } from './article.contracts' 8 | 9 | export type Article = z.infer 10 | export type Articles = z.infer 11 | export type FilterQuery = z.infer 12 | export type InfiniteArticles = InfiniteData 13 | -------------------------------------------------------------------------------- /playwright/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-tests", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "@playwright/test": "^1.46.1", 7 | "@types/node": "^22.5.3" 8 | }, 9 | "scripts": { 10 | "test": "npx playwright test", 11 | "test:headed": "npx playwright test --headed", 12 | "test:install": "npx playwright install" 13 | }, 14 | "dependencies": { 15 | "dotenv": "^16.4.7", 16 | "wait-on": "^8.0.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/app/Contracts/JwtParserInterface.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $except = [ 17 | 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /server/app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | public function hosts() 17 | { 18 | return [ 19 | $this->allSubdomainsOfApplicationUrl(), 20 | ]; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-router/react-router.types.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | import { 3 | EditorPageArgsSchema, 4 | ProfilePageArgsSchema, 5 | SlugPageParamsSchema, 6 | UsernamePageParamsSchema, 7 | } from './react-router.contracts' 8 | 9 | export type SlugPageParams = z.infer 10 | export type UsernamePageParams = z.infer 11 | export type ProfilePageData = z.infer 12 | export type EditorPageData = z.infer 13 | -------------------------------------------------------------------------------- /server/app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | protected $except = [ 17 | 'current_password', 18 | 'password', 19 | 'password_confirmation', 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "mix", 6 | "watch": "mix watch", 7 | "watch-poll": "mix watch -- --watch-options-poll=1000", 8 | "hot": "mix watch --hot", 9 | "prod": "npm run production", 10 | "production": "mix --production" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.25", 14 | "laravel-mix": "^6.0.6", 15 | "lodash": "^4.17.19", 16 | "postcss": "^8.1.14" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public function list() 19 | { 20 | return new TagsCollection(Tag::all()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /client/src/6shared/lib/react-router/react-router.contracts.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const SlugPageParamsSchema = z.object({ slug: z.string() }) 4 | export const UsernamePageParamsSchema = z.object({ username: z.string() }) 5 | 6 | export const ProfilePageArgsSchema = z.object({ 7 | request: z.custom(), 8 | params: UsernamePageParamsSchema, 9 | context: z.any().optional(), 10 | }) 11 | 12 | export const EditorPageArgsSchema = z.object({ 13 | request: z.custom(), 14 | params: SlugPageParamsSchema, 15 | context: z.any().optional(), 16 | }) 17 | -------------------------------------------------------------------------------- /client/src/6shared/lib/zustand/zustand.lib.ts: -------------------------------------------------------------------------------- 1 | import { StoreApi, UseBoundStore } from 'zustand' 2 | 3 | type WithSelectors = S extends { getState: () => infer T } 4 | ? S & { use: { [K in keyof T]: () => T[K] } } 5 | : never 6 | 7 | export const createSelectors = >>( 8 | _store: S, 9 | ) => { 10 | const store = _store as WithSelectors 11 | store.use = {} 12 | Object.keys(store.getState()).forEach((key) => { 13 | ;(store.use as any)[key] = () => store((s) => s[key as keyof typeof s]) 14 | }) 15 | 16 | return store 17 | } 18 | -------------------------------------------------------------------------------- /server/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | 6 |
    7 |
    8 |
    9 |

    Your Settings

    10 | 11 | 12 | 13 |
    14 | 15 | 16 |
    17 |
    18 |
    19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /server/routes/web.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Conduit 6 | 10 | 15 | 19 | 20 | 21 |
    22 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /server/webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel applications. By default, we are compiling the CSS 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | mix.js('resources/js/app.js', 'public/js') 15 | .postCss('resources/css/app.css', 'public/css', [ 16 | // 17 | ]); 18 | -------------------------------------------------------------------------------- /client/src/2pages/layouts/guest.layout.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet } from 'react-router-dom' 2 | import { 3 | Footer, 4 | BrandLink, 5 | SignInLink, 6 | } from './layout.ui' 7 | 8 | export function GuestLayout() { 9 | return ( 10 | <> 11 | 22 | 23 |