diff --git a/.env.example b/.env.example index cea9a39..cfcd647 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,7 @@ -REACT_APP_API_URL=http://localhost:8000 \ No newline at end of file +REACT_APP_API_URL=http://localhost:8000 + +PUBLIC_URL=https://sdn-host.net/public/ + +CDN_HOST=some-ftp-host.com +CDN_LOGIN=gitcom +CDN_PASSWORD=your-password \ No newline at end of file diff --git a/README.md b/README.md index 51df806..58255f3 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ This is the main frontend application hosted at [gitcom.org](https://gitcom.org) +![](public/img/screenshots/screenshot.jpg?raw=true) + ### Brief description This platform can help: 1. users: it will provide an easy way for users to support independent developers via subscription or donations and get rewards in return diff --git a/package.json b/package.json index 406ecd7..3d9512e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitcom-front", - "version": "0.12.0", + "version": "0.13.0", "private": true, "dependencies": { "@azure/ms-rest-js": "^2.0.5", @@ -46,5 +46,6 @@ "last 1 firefox version", "last 1 safari version" ] - } + }, + "devDependencies": {} } diff --git a/public/img/icon/product/discord_full.svg b/public/img/icon/product/discord_full.svg new file mode 100644 index 0000000..22dd113 --- /dev/null +++ b/public/img/icon/product/discord_full.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/img/screenshots/screenshot.jpg b/public/img/screenshots/screenshot.jpg new file mode 100644 index 0000000..9e24df2 Binary files /dev/null and b/public/img/screenshots/screenshot.jpg differ diff --git a/public/index.html b/public/index.html index 1560d76..a48e3b1 100644 --- a/public/index.html +++ b/public/index.html @@ -15,6 +15,7 @@
+ - - - - diff --git a/public/js/lazy_init.js b/public/js/lazy_init.js new file mode 100644 index 0000000..7ae04db --- /dev/null +++ b/public/js/lazy_init.js @@ -0,0 +1,28 @@ +!function () { + var t = window.driftt = window.drift = window.driftt || []; + if (!t.init) { + if (t.invoked) return void (window.console && console.error && console.error("Drift snippet included twice.")); + t.invoked = !0, t.methods = ["identify", "config", "track", "reset", "debug", "show", "ping", "page", "hide", "off", "on"], + t.factory = function (e) { + return function () { + var n = Array.prototype.slice.call(arguments); + return n.unshift(e), t.push(n), t; + }; + }, t.methods.forEach(function (e) { + t[e] = t.factory(e); + }), t.load = function (t) { + var e = 3e5, n = Math.ceil(new Date() / e) * e, o = document.createElement("script"); + o.type = "text/javascript", o.async = !0, o.crossorigin = "anonymous", o.src = "https://js.driftt.com/include/" + n + "/" + t + ".js"; + var i = document.getElementsByTagName("script")[0]; + i.parentNode.insertBefore(o, i); + }; + } +}(); +drift.SNIPPET_VERSION = '0.3.1'; +drift.load('kmcg4ky4chmh'); + +window.dataLayer = window.dataLayer || []; +function gtag(){dataLayer.push(arguments);} +gtag('js', new Date()); + +gtag('config', 'UA-153205738-3'); \ No newline at end of file diff --git a/public/sitemap.xml b/public/sitemap.xml index b177238..8b52db9 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -7,14 +7,14 @@ https://gitcom.org/ - 2019-12-02T01:47:20+00:00 1.00 - https://gitcom.org/login - 2019-12-02T01:47:20+00:00 - 0.80 + https://gitcom.org/explore/projects + 0.90 + + + https://gitcom.org/explore/cards + 0.90 - - \ No newline at end of file diff --git a/scripts/deploy/prod/copy_build.sh b/scripts/deploy/prod/copy_build.sh deleted file mode 100755 index 24d33ac..0000000 --- a/scripts/deploy/prod/copy_build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -scp -r build "$1":/usr/local/gitcom/frontend/app/tmp -echo "Build was copied to production!" \ No newline at end of file diff --git a/scripts/deploy/prod/deploy.sh b/scripts/deploy/prod/deploy.sh index c2b3188..17ac557 100755 --- a/scripts/deploy/prod/deploy.sh +++ b/scripts/deploy/prod/deploy.sh @@ -1,3 +1,4 @@ #!/bin/bash -rm -rf /usr/local/gitcom/frontend/app/build/* -cp -avr /usr/local/gitcom/frontend/app/tmp/build /usr/local/gitcom/frontend/app/ \ No newline at end of file +python3 scripts/deploy/prod/stages/cdn_upload.py +./scripts/deploy/prod/stages/copy_build.sh $1 +ssh $1 "bash -s" < ./scripts/deploy/prod/stages/deploy.sh \ No newline at end of file diff --git a/scripts/deploy/prod/stages/cdn_upload.py b/scripts/deploy/prod/stages/cdn_upload.py new file mode 100644 index 0000000..59e3c66 --- /dev/null +++ b/scripts/deploy/prod/stages/cdn_upload.py @@ -0,0 +1,62 @@ +import os.path, os +from ftplib import FTP, error_perm + +from dotenv import load_dotenv +load_dotenv() + +load_dotenv(verbose=True) + +from pathlib import Path +env_path = Path('.') / '.env' + +host = os.getenv("CDN_HOST") +port = 21 + +ftp = FTP() +ftp.connect(host,port) +ftp.login(os.getenv("CDN_LOGIN"), os.getenv("CDN_PASSWORD")) + +def count_files(path): + files_count = 0 + for name in os.listdir(path): + localpath = os.path.join(path, name) + if os.path.isfile(localpath): + files_count += 1 + elif os.path.isdir(localpath): + files_count += 1 + files_count += count_files(localpath) + return files_count + +def upload_folder(ftp, path, prefix = None): + if prefix is not None: + progress = 0 + print("total files: " + str(count_files(path)) + " in " + path) + root_dir = "/gitcom/" + prefix + try: + ftp.mkd(root_dir) + except error_perm as e: + print(e) + ftp.cwd(root_dir) + + for name in os.listdir(path): + localpath = os.path.join(path, name) + if os.path.isfile(localpath): + print("Storing ", name, localpath) + ftp.storbinary('STOR ' + name, open(localpath,'rb')) + elif os.path.isdir(localpath): + print("MKD", name) + + try: + ftp.mkd(name) + except error_perm as e: + print(e) + + print("CWD", name) + ftp.cwd(name) + upload_folder(ftp, localpath) + print("CWD", "..") + ftp.cwd("..") + +upload_folder(ftp, "build", "public/") + +ftp.quit() diff --git a/scripts/deploy/prod/stages/copy_build.sh b/scripts/deploy/prod/stages/copy_build.sh new file mode 100755 index 0000000..a0fddaa --- /dev/null +++ b/scripts/deploy/prod/stages/copy_build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +scp -r build "$1":/usr/local/gitcom/frontend/app/tmp \ No newline at end of file diff --git a/scripts/deploy/prod/stages/deploy.sh b/scripts/deploy/prod/stages/deploy.sh new file mode 100644 index 0000000..c2b3188 --- /dev/null +++ b/scripts/deploy/prod/stages/deploy.sh @@ -0,0 +1,3 @@ +#!/bin/bash +rm -rf /usr/local/gitcom/frontend/app/build/* +cp -avr /usr/local/gitcom/frontend/app/tmp/build /usr/local/gitcom/frontend/app/ \ No newline at end of file diff --git a/src/AppRoot.tsx b/src/AppRoot.tsx index 5e8be92..1782223 100644 --- a/src/AppRoot.tsx +++ b/src/AppRoot.tsx @@ -19,6 +19,19 @@ window.AppConfig = config; // @ts-ignore window.App = new App(); +async function loadScriptAsync(url: string) { + const script = document.createElement("script"); + script.src = url; + script.async = true; + document.body.appendChild(script); +} + +loadScriptAsync("https://momentjs.com/downloads/moment.min.js").then(() => {}); + +setTimeout(() => { + loadScriptAsync("/js/lazy_init.js").then(() => {}); +}, 1000); + function AppRoot() { return (
diff --git a/src/Routes.tsx b/src/Routes.tsx index 51539d0..69e6241 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -1,37 +1,42 @@ -import React from "react"; +import React, { Suspense, lazy } from "react"; import { Route, Switch } from 'react-router-dom'; -import IndexLayout from "./layouts/index/IndexLayout"; -import LoginLayout from "./layouts/auth/login/LoginLayout"; -import HomeMainLayout from "./layouts/home/main/HomeMainLayout"; -import AccountLayout from "./layouts/account/account/AccountLayout"; -import BillingLayout from "./layouts/account/billing/BillingLayout"; -import SettingsLayout from "./layouts/account/settings/SettingsLayout"; -import HomeIntegrationsLayout from "./layouts/home/integrations/HomeIntegrationsLayout"; -import LoginWithGitLabLayout from "./layouts/auth/external/gitlab/logInWith/LoginWithGitLabLayout"; -import LoginWithGitHubLayout from "./layouts/auth/external/github/loginWith/LoginWithGitHubLayout"; -import ExternalRepoLayout from "./layouts/repos/external/default/ExternalRepoLayout"; -import ProjectImportLayout from "./layouts/import/project/ProjectImportLayout"; -import ProjectPage from "./layouts/entity/project/view/ProjectPage"; -import BoardPage from "./layouts/entity/board/page/BoardPage"; -import SubscriptionLayout from "./layouts/account/subscription/SubscriptionLayout"; -import LibraryLayout from "./layouts/account/library/LibraryLayout"; -import EditProjectLayout from "./layouts/entity/project/edit/EditProjectLayout"; -import ProjectPricingLayout from "./layouts/entity/project/pricing/view/ProjectPricingLayout"; -import RegisterLayout from "./layouts/auth/register/RegisterLayout"; -import ConfirmEmailLayout from "./layouts/auth/confirmEmail/ConfirmEmailLayout"; -import NotFoundLayout from "./layouts/404/NotFoundLayout"; -import HelpLayout from "./layouts/help/HelpLayout"; -import EditProjectPricingLayout from "./layouts/entity/project/pricing/edit/EditProjectPricingLayout"; -import WithdrawalsLayout from "./layouts/account/withdrawals/WithdrawalsLayout"; -import DeveloperQuickstartLayout from "./layouts/quickstart/developer/DeveloperQuickstartLayout"; +import { Icon } from 'antd'; +import ExploreCardsLayout from "./layouts/explore/cards/ExploreCardsLayout"; +const IndexLayout = lazy(() => import("./layouts/index/IndexLayout")); +const LoginWithGitLabLayout = lazy(() => import("./layouts/auth/external/gitlab/logInWith/LoginWithGitLabLayout")); +const LoginWithGitHubLayout = lazy(() => import("./layouts/auth/external/github/loginWith/LoginWithGitHubLayout")); +const ExternalRepoLayout = lazy(() => import("./layouts/repos/external/default/ExternalRepoLayout")); +const ProjectPage = lazy(() => import("./layouts/entity/project/view/ProjectPage")); +const BoardPage = lazy(() => import("./layouts/entity/board/page/BoardPage")); +const SubscriptionLayout = lazy(() => import("./layouts/account/subscription/SubscriptionLayout")); +const LibraryLayout = lazy(() => import("./layouts/account/library/LibraryLayout")); +const EditProjectLayout = lazy(() => import("./layouts/entity/project/edit/EditProjectLayout")); +const ProjectPricingLayout = lazy(() => import("./layouts/entity/project/pricing/view/ProjectPricingLayout")); +const RegisterLayout = lazy(() => import("./layouts/auth/register/RegisterLayout")); +const ConfirmEmailLayout = lazy(() => import("./layouts/auth/confirmEmail/ConfirmEmailLayout")); +const NotFoundLayout = lazy(() => import("./layouts/404/NotFoundLayout")); +const HelpLayout = lazy(() => import("./layouts/help/HelpLayout")); +const EditProjectPricingLayout = lazy(() => import("./layouts/entity/project/pricing/edit/EditProjectPricingLayout")); +const WithdrawalsLayout = lazy(() => import("./layouts/account/withdrawals/WithdrawalsLayout")); +const DeveloperQuickstartLayout = lazy(() => import("./layouts/quickstart/developer/DeveloperQuickstartLayout")); +const ProjectCardsLayout = lazy(() => import("./layouts/entity/project/sub_pages/cards/ProjectCardsLayout")); +const ExploreProjectsLayout = lazy(() => import("./layouts/explore/projects/ExploreProjectsLayout")); +const LoginLayout = lazy(() => import("./layouts/auth/login/LoginLayout")); +const HomeMainLayout = lazy(() => import("./layouts/home/main/HomeMainLayout")); +const AccountLayout = lazy(() => import("./layouts/account/account/AccountLayout")); +const BillingLayout = lazy(() => import("./layouts/account/billing/BillingLayout")); +const SettingsLayout = lazy(() => import("./layouts/account/settings/SettingsLayout")); +const HomeIntegrationsLayout = lazy(() => import("./layouts/home/integrations/HomeIntegrationsLayout")); class Routes extends React.Component { render() { return ( -
+ + }> - {/* Auth */} @@ -62,9 +67,10 @@ class Routes extends React.Component { - {/* External import routes */} + {/* Explore */} - + + {/* Project */} @@ -76,9 +82,11 @@ class Routes extends React.Component { + + -
+ ); } } diff --git a/src/app.css b/src/app.css index 376a5f4..ffddaf4 100644 --- a/src/app.css +++ b/src/app.css @@ -143,6 +143,10 @@ p, b { color: white !important; } +.ant-modal-body img { + max-width: 100%; +} + @media only screen and (min-width: 767px) { .desktop-zero-height { height: 0; diff --git a/src/client/bindings.ts b/src/client/bindings.ts index d83b97b..1f7eb95 100644 --- a/src/client/bindings.ts +++ b/src/client/bindings.ts @@ -8,6 +8,7 @@ export class ProjectModel { creator_guid?: string; base_uri?: string; stars_count?: number; + confirmed?: boolean; created_at?: string; updated_at?: string; } @@ -174,3 +175,9 @@ export class ProjectPost { created_at?: string; updated_at?: string; } + +export class Image { + guid?: string; + url?: string; + created_at?: string; +} diff --git a/src/client/models/index.ts b/src/client/models/index.ts index 1ae4137..cef6e65 100644 --- a/src/client/models/index.ts +++ b/src/client/models/index.ts @@ -48,6 +48,7 @@ export interface Project { creatorGuid?: string; baseUri?: string; starsCount?: number; + confirmed?: boolean; createdAt?: string; updatedAt?: string; } @@ -261,6 +262,15 @@ export interface WithdrawalRequest { createdAt?: string; } +/** + * An interface representing Image. + */ +export interface Image { + guid?: string; + url?: string; + createdAt?: string; +} + /** * An interface representing GetLoginOKResponseData. */ @@ -540,6 +550,20 @@ export interface GetProjectPostsOKResponse { data?: GetProjectPostsOKResponseData; } +/** + * An interface representing GetProjectImagesOKResponseData. + */ +export interface GetProjectImagesOKResponseData { + images?: Image; +} + +/** + * An interface representing GetProjectImagesOKResponse. + */ +export interface GetProjectImagesOKResponse { + data?: GetProjectImagesOKResponseData; +} + /** * An interface representing GetBoardOKResponseData. */ @@ -582,6 +606,52 @@ export interface GetColumnCardsOKResponse { data?: GetColumnCardsOKResponseData; } +/** + * An interface representing GetProjectCardsOKResponseData. + */ +export interface GetProjectCardsOKResponseData { + cards?: Card[]; +} + +/** + * An interface representing GetProjectCardsOKResponseMeta. + */ +export interface GetProjectCardsOKResponseMeta { + currentPage?: number; + pagesCount?: number; +} + +/** + * An interface representing GetProjectCardsOKResponse. + */ +export interface GetProjectCardsOKResponse { + data?: GetProjectCardsOKResponseData; + meta?: GetProjectCardsOKResponseMeta; +} + +/** + * An interface representing GetCardsOKResponseData. + */ +export interface GetCardsOKResponseData { + cards?: Card[]; +} + +/** + * An interface representing GetCardsOKResponseMeta. + */ +export interface GetCardsOKResponseMeta { + currentPage?: number; + pagesCount?: number; +} + +/** + * An interface representing GetCardsOKResponse. + */ +export interface GetCardsOKResponse { + data?: GetCardsOKResponseData; + meta?: GetCardsOKResponseMeta; +} + /** * An interface representing GetProjectByAliasOKResponseData. */ @@ -610,6 +680,20 @@ export interface CreateCardCreatedResponse { data?: CreateCardCreatedResponseData; } +/** + * An interface representing GetCardOKResponseData. + */ +export interface GetCardOKResponseData { + card?: Card; +} + +/** + * An interface representing GetCardOKResponse. + */ +export interface GetCardOKResponse { + data?: GetCardOKResponseData; +} + /** * An interface representing EditCardOKResponseData. */ @@ -946,11 +1030,20 @@ export interface GetNewestProjectsOKResponseData { projects?: Project[]; } +/** + * An interface representing GetNewestProjectsOKResponseMeta. + */ +export interface GetNewestProjectsOKResponseMeta { + currentPage?: number; + pagesCount?: number; +} + /** * An interface representing GetNewestProjectsOKResponse. */ export interface GetNewestProjectsOKResponse { data?: GetNewestProjectsOKResponseData; + meta?: GetNewestProjectsOKResponseMeta; } /** @@ -967,6 +1060,20 @@ export interface GetRandomProjectsOKResponse { data?: GetRandomProjectsOKResponseData; } +/** + * An interface representing GetUserProjectsOKResponseData. + */ +export interface GetUserProjectsOKResponseData { + projects?: Project[]; +} + +/** + * An interface representing GetUserProjectsOKResponse. + */ +export interface GetUserProjectsOKResponse { + data?: GetUserProjectsOKResponseData; +} + /** * An interface representing GetProjectProductsOKResponseData. */ @@ -1108,6 +1215,27 @@ export interface SupportHubApiEditProjectOptionalParams extends msRest.RequestOp description?: string; } +/** + * Optional Parameters. + */ +export interface SupportHubApiGetColumnCardsOptionalParams extends msRest.RequestOptionsBase { + page?: number; +} + +/** + * Optional Parameters. + */ +export interface SupportHubApiGetProjectCardsOptionalParams extends msRest.RequestOptionsBase { + page?: number; +} + +/** + * Optional Parameters. + */ +export interface SupportHubApiGetCardsOptionalParams extends msRest.RequestOptionsBase { + page?: number; +} + /** * Optional Parameters. */ @@ -1126,6 +1254,13 @@ export interface SupportHubApiEditCardOptionalParams extends msRest.RequestOptio columnGuid?: string; } +/** + * Optional Parameters. + */ +export interface SupportHubApiGetNewestProjectsOptionalParams extends msRest.RequestOptionsBase { + page?: number; +} + /** * Optional Parameters. */ @@ -1597,6 +1732,26 @@ export type GetProjectPostsResponse = GetProjectPostsOKResponse & { }; }; +/** + * Contains response data for the getProjectImages operation. + */ +export type GetProjectImagesResponse = GetProjectImagesOKResponse & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: GetProjectImagesOKResponse; + }; +}; + /** * Contains response data for the getBoard operation. */ @@ -1657,6 +1812,46 @@ export type GetColumnCardsResponse = GetColumnCardsOKResponse & { }; }; +/** + * Contains response data for the getProjectCards operation. + */ +export type GetProjectCardsResponse = GetProjectCardsOKResponse & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: GetProjectCardsOKResponse; + }; +}; + +/** + * Contains response data for the getCards operation. + */ +export type GetCardsResponse = GetCardsOKResponse & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: GetCardsOKResponse; + }; +}; + /** * Contains response data for the getProjectByAlias operation. */ @@ -1697,6 +1892,26 @@ export type CreateCardResponse = CreateCardCreatedResponse & { }; }; +/** + * Contains response data for the getCard operation. + */ +export type GetCardResponse = GetCardOKResponse & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: GetCardOKResponse; + }; +}; + /** * Contains response data for the editCard operation. */ @@ -2197,6 +2412,26 @@ export type GetRandomProjectsResponse = GetRandomProjectsOKResponse & { }; }; +/** + * Contains response data for the getUserProjects operation. + */ +export type GetUserProjectsResponse = GetUserProjectsOKResponse & { + /** + * The underlying HTTP response. + */ + _response: msRest.HttpResponse & { + /** + * The response body as text (string format) + */ + bodyAsText: string; + + /** + * The response body as parsed JSON or XML + */ + parsedBody: GetUserProjectsOKResponse; + }; +}; + /** * Contains response data for the getProjectProducts operation. */ diff --git a/src/client/models/mappers.ts b/src/client/models/mappers.ts index 5645689..54e3234 100644 --- a/src/client/models/mappers.ts +++ b/src/client/models/mappers.ts @@ -145,6 +145,12 @@ export const Project: msRest.CompositeMapper = { name: "Number" } }, + confirmed: { + serializedName: "confirmed", + type: { + name: "Boolean" + } + }, createdAt: { serializedName: "created_at", type: { @@ -848,6 +854,34 @@ export const WithdrawalRequest: msRest.CompositeMapper = { } }; +export const Image: msRest.CompositeMapper = { + serializedName: "Image", + type: { + name: "Composite", + className: "Image", + modelProperties: { + guid: { + serializedName: "guid", + type: { + name: "String" + } + }, + url: { + serializedName: "url", + type: { + name: "String" + } + }, + createdAt: { + serializedName: "created_at", + type: { + name: "String" + } + } + } + } +}; + export const GetLoginOKResponseData: msRest.CompositeMapper = { serializedName: "GetLoginOKResponse_data", type: { @@ -1549,6 +1583,40 @@ export const GetProjectPostsOKResponse: msRest.CompositeMapper = { } }; +export const GetProjectImagesOKResponseData: msRest.CompositeMapper = { + serializedName: "GetProjectImagesOKResponse_data", + type: { + name: "Composite", + className: "GetProjectImagesOKResponseData", + modelProperties: { + images: { + serializedName: "images", + type: { + name: "Composite", + className: "Image" + } + } + } + } +}; + +export const GetProjectImagesOKResponse: msRest.CompositeMapper = { + serializedName: "GetProjectImagesOKResponse", + type: { + name: "Composite", + className: "GetProjectImagesOKResponse", + modelProperties: { + data: { + serializedName: "data", + type: { + name: "Composite", + className: "GetProjectImagesOKResponseData" + } + } + } + } +}; + export const GetBoardOKResponseData: msRest.CompositeMapper = { serializedName: "GetBoardOKResponse_data", type: { @@ -1661,6 +1729,142 @@ export const GetColumnCardsOKResponse: msRest.CompositeMapper = { } }; +export const GetProjectCardsOKResponseData: msRest.CompositeMapper = { + serializedName: "GetProjectCardsOKResponse_data", + type: { + name: "Composite", + className: "GetProjectCardsOKResponseData", + modelProperties: { + cards: { + serializedName: "cards", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "Card" + } + } + } + } + } + } +}; + +export const GetProjectCardsOKResponseMeta: msRest.CompositeMapper = { + serializedName: "GetProjectCardsOKResponse_meta", + type: { + name: "Composite", + className: "GetProjectCardsOKResponseMeta", + modelProperties: { + currentPage: { + serializedName: "current_page", + type: { + name: "Number" + } + }, + pagesCount: { + serializedName: "pages_count", + type: { + name: "Number" + } + } + } + } +}; + +export const GetProjectCardsOKResponse: msRest.CompositeMapper = { + serializedName: "GetProjectCardsOKResponse", + type: { + name: "Composite", + className: "GetProjectCardsOKResponse", + modelProperties: { + data: { + serializedName: "data", + type: { + name: "Composite", + className: "GetProjectCardsOKResponseData" + } + }, + meta: { + serializedName: "meta", + type: { + name: "Composite", + className: "GetProjectCardsOKResponseMeta" + } + } + } + } +}; + +export const GetCardsOKResponseData: msRest.CompositeMapper = { + serializedName: "GetCardsOKResponse_data", + type: { + name: "Composite", + className: "GetCardsOKResponseData", + modelProperties: { + cards: { + serializedName: "cards", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "Card" + } + } + } + } + } + } +}; + +export const GetCardsOKResponseMeta: msRest.CompositeMapper = { + serializedName: "GetCardsOKResponse_meta", + type: { + name: "Composite", + className: "GetCardsOKResponseMeta", + modelProperties: { + currentPage: { + serializedName: "current_page", + type: { + name: "Number" + } + }, + pagesCount: { + serializedName: "pages_count", + type: { + name: "Number" + } + } + } + } +}; + +export const GetCardsOKResponse: msRest.CompositeMapper = { + serializedName: "GetCardsOKResponse", + type: { + name: "Composite", + className: "GetCardsOKResponse", + modelProperties: { + data: { + serializedName: "data", + type: { + name: "Composite", + className: "GetCardsOKResponseData" + } + }, + meta: { + serializedName: "meta", + type: { + name: "Composite", + className: "GetCardsOKResponseMeta" + } + } + } + } +}; + export const GetProjectByAliasOKResponseData: msRest.CompositeMapper = { serializedName: "GetProjectByAliasOKResponse_data", type: { @@ -1729,6 +1933,40 @@ export const CreateCardCreatedResponse: msRest.CompositeMapper = { } }; +export const GetCardOKResponseData: msRest.CompositeMapper = { + serializedName: "GetCardOKResponse_data", + type: { + name: "Composite", + className: "GetCardOKResponseData", + modelProperties: { + card: { + serializedName: "card", + type: { + name: "Composite", + className: "Card" + } + } + } + } +}; + +export const GetCardOKResponse: msRest.CompositeMapper = { + serializedName: "GetCardOKResponse", + type: { + name: "Composite", + className: "GetCardOKResponse", + modelProperties: { + data: { + serializedName: "data", + type: { + name: "Composite", + className: "GetCardOKResponseData" + } + } + } + } +}; + export const EditCardOKResponseData: msRest.CompositeMapper = { serializedName: "EditCardOKResponse_data", type: { @@ -2591,6 +2829,28 @@ export const GetNewestProjectsOKResponseData: msRest.CompositeMapper = { } }; +export const GetNewestProjectsOKResponseMeta: msRest.CompositeMapper = { + serializedName: "GetNewestProjectsOKResponse_meta", + type: { + name: "Composite", + className: "GetNewestProjectsOKResponseMeta", + modelProperties: { + currentPage: { + serializedName: "current_page", + type: { + name: "Number" + } + }, + pagesCount: { + serializedName: "pages_count", + type: { + name: "Number" + } + } + } + } +}; + export const GetNewestProjectsOKResponse: msRest.CompositeMapper = { serializedName: "GetNewestProjectsOKResponse", type: { @@ -2603,6 +2863,13 @@ export const GetNewestProjectsOKResponse: msRest.CompositeMapper = { name: "Composite", className: "GetNewestProjectsOKResponseData" } + }, + meta: { + serializedName: "meta", + type: { + name: "Composite", + className: "GetNewestProjectsOKResponseMeta" + } } } } @@ -2647,6 +2914,45 @@ export const GetRandomProjectsOKResponse: msRest.CompositeMapper = { } }; +export const GetUserProjectsOKResponseData: msRest.CompositeMapper = { + serializedName: "GetUserProjectsOKResponse_data", + type: { + name: "Composite", + className: "GetUserProjectsOKResponseData", + modelProperties: { + projects: { + serializedName: "projects", + type: { + name: "Sequence", + element: { + type: { + name: "Composite", + className: "Project" + } + } + } + } + } + } +}; + +export const GetUserProjectsOKResponse: msRest.CompositeMapper = { + serializedName: "GetUserProjectsOKResponse", + type: { + name: "Composite", + className: "GetUserProjectsOKResponse", + modelProperties: { + data: { + serializedName: "data", + type: { + name: "Composite", + className: "GetUserProjectsOKResponseData" + } + } + } + } +}; + export const GetProjectProductsOKResponseData: msRest.CompositeMapper = { serializedName: "GetProjectProductsOKResponse_data", type: { diff --git a/src/client/models/parameters.ts b/src/client/models/parameters.ts index be73951..d23a1b6 100644 --- a/src/client/models/parameters.ts +++ b/src/client/models/parameters.ts @@ -312,6 +312,18 @@ export const owner: msRest.OperationQueryParameter = { } } }; +export const page: msRest.OperationQueryParameter = { + parameterPath: [ + "options", + "page" + ], + mapper: { + serializedName: "page", + type: { + name: "Number" + } + } +}; export const password0: msRest.OperationQueryParameter = { parameterPath: [ "options", @@ -426,6 +438,16 @@ export const usdPrice: msRest.OperationQueryParameter = { } } }; +export const userGuid: msRest.OperationQueryParameter = { + parameterPath: "userGuid", + mapper: { + required: true, + serializedName: "user_guid", + type: { + name: "String" + } + } +}; export const useUrl: msRest.OperationQueryParameter = { parameterPath: "useUrl", mapper: { diff --git a/src/client/supportHubApi.ts b/src/client/supportHubApi.ts index 5f37a9e..3a59051 100644 --- a/src/client/supportHubApi.ts +++ b/src/client/supportHubApi.ts @@ -480,6 +480,33 @@ class SupportHubApi extends SupportHubApiContext { callback) as Promise; } + /** + * @param projectGuid + * @param [options] The optional parameters + * @returns Promise + */ + getProjectImages(projectGuid: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param projectGuid + * @param callback The callback + */ + getProjectImages(projectGuid: string, callback: msRest.ServiceCallback): void; + /** + * @param projectGuid + * @param options The optional parameters + * @param callback The callback + */ + getProjectImages(projectGuid: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + getProjectImages(projectGuid: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.sendOperationRequest( + { + projectGuid, + options + }, + getProjectImagesOperationSpec, + callback) as Promise; + } + /** * @param boardGuid Board Guid * @param [options] The optional parameters @@ -539,7 +566,7 @@ class SupportHubApi extends SupportHubApiContext { * @param [options] The optional parameters * @returns Promise */ - getColumnCards(columnGuid: string, options?: msRest.RequestOptionsBase): Promise; + getColumnCards(columnGuid: string, options?: Models.SupportHubApiGetColumnCardsOptionalParams): Promise; /** * @param columnGuid * @param callback The callback @@ -550,8 +577,8 @@ class SupportHubApi extends SupportHubApiContext { * @param options The optional parameters * @param callback The callback */ - getColumnCards(columnGuid: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; - getColumnCards(columnGuid: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + getColumnCards(columnGuid: string, options: Models.SupportHubApiGetColumnCardsOptionalParams, callback: msRest.ServiceCallback): void; + getColumnCards(columnGuid: string, options?: Models.SupportHubApiGetColumnCardsOptionalParams | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { return this.sendOperationRequest( { columnGuid, @@ -561,6 +588,56 @@ class SupportHubApi extends SupportHubApiContext { callback) as Promise; } + /** + * @param projectGuid + * @param [options] The optional parameters + * @returns Promise + */ + getProjectCards(projectGuid: string, options?: Models.SupportHubApiGetProjectCardsOptionalParams): Promise; + /** + * @param projectGuid + * @param callback The callback + */ + getProjectCards(projectGuid: string, callback: msRest.ServiceCallback): void; + /** + * @param projectGuid + * @param options The optional parameters + * @param callback The callback + */ + getProjectCards(projectGuid: string, options: Models.SupportHubApiGetProjectCardsOptionalParams, callback: msRest.ServiceCallback): void; + getProjectCards(projectGuid: string, options?: Models.SupportHubApiGetProjectCardsOptionalParams | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.sendOperationRequest( + { + projectGuid, + options + }, + getProjectCardsOperationSpec, + callback) as Promise; + } + + /** + * @param [options] The optional parameters + * @returns Promise + */ + getCards(options?: Models.SupportHubApiGetCardsOptionalParams): Promise; + /** + * @param callback The callback + */ + getCards(callback: msRest.ServiceCallback): void; + /** + * @param options The optional parameters + * @param callback The callback + */ + getCards(options: Models.SupportHubApiGetCardsOptionalParams, callback: msRest.ServiceCallback): void; + getCards(options?: Models.SupportHubApiGetCardsOptionalParams | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.sendOperationRequest( + { + options + }, + getCardsOperationSpec, + callback) as Promise; + } + /** * @param owner * @param alias @@ -627,6 +704,33 @@ class SupportHubApi extends SupportHubApiContext { callback) as Promise; } + /** + * @param cardGuid + * @param [options] The optional parameters + * @returns Promise + */ + getCard(cardGuid: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param cardGuid + * @param callback The callback + */ + getCard(cardGuid: string, callback: msRest.ServiceCallback): void; + /** + * @param cardGuid + * @param options The optional parameters + * @param callback The callback + */ + getCard(cardGuid: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + getCard(cardGuid: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.sendOperationRequest( + { + cardGuid, + options + }, + getCardOperationSpec, + callback) as Promise; + } + /** * @param apiToken * @param cardGuid @@ -1319,7 +1423,7 @@ class SupportHubApi extends SupportHubApiContext { * @param [options] The optional parameters * @returns Promise */ - getNewestProjects(options?: msRest.RequestOptionsBase): Promise; + getNewestProjects(options?: Models.SupportHubApiGetNewestProjectsOptionalParams): Promise; /** * @param callback The callback */ @@ -1328,8 +1432,8 @@ class SupportHubApi extends SupportHubApiContext { * @param options The optional parameters * @param callback The callback */ - getNewestProjects(options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; - getNewestProjects(options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + getNewestProjects(options: Models.SupportHubApiGetNewestProjectsOptionalParams, callback: msRest.ServiceCallback): void; + getNewestProjects(options?: Models.SupportHubApiGetNewestProjectsOptionalParams | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { return this.sendOperationRequest( { options @@ -1361,6 +1465,33 @@ class SupportHubApi extends SupportHubApiContext { callback) as Promise; } + /** + * @param userGuid + * @param [options] The optional parameters + * @returns Promise + */ + getUserProjects(userGuid: string, options?: msRest.RequestOptionsBase): Promise; + /** + * @param userGuid + * @param callback The callback + */ + getUserProjects(userGuid: string, callback: msRest.ServiceCallback): void; + /** + * @param userGuid + * @param options The optional parameters + * @param callback The callback + */ + getUserProjects(userGuid: string, options: msRest.RequestOptionsBase, callback: msRest.ServiceCallback): void; + getUserProjects(userGuid: string, options?: msRest.RequestOptionsBase | msRest.ServiceCallback, callback?: msRest.ServiceCallback): Promise { + return this.sendOperationRequest( + { + userGuid, + options + }, + getUserProjectsOperationSpec, + callback) as Promise; + } + /** * @param projectGuid * @param [options] The optional parameters @@ -1823,6 +1954,21 @@ const getProjectPostsOperationSpec: msRest.OperationSpec = { serializer }; +const getProjectImagesOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "api/v1/project/images/get", + queryParameters: [ + Parameters.projectGuid + ], + responses: { + 200: { + bodyMapper: Mappers.GetProjectImagesOKResponse + }, + default: {} + }, + serializer +}; + const getBoardOperationSpec: msRest.OperationSpec = { httpMethod: "GET", path: "api/v1/board/get", @@ -1857,7 +2003,8 @@ const getColumnCardsOperationSpec: msRest.OperationSpec = { httpMethod: "GET", path: "api/v1/board_column/cards/get", queryParameters: [ - Parameters.columnGuid0 + Parameters.columnGuid0, + Parameters.page ], responses: { 200: { @@ -1868,6 +2015,37 @@ const getColumnCardsOperationSpec: msRest.OperationSpec = { serializer }; +const getProjectCardsOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "api/v1/project/cards/get", + queryParameters: [ + Parameters.projectGuid, + Parameters.page + ], + responses: { + 200: { + bodyMapper: Mappers.GetProjectCardsOKResponse + }, + default: {} + }, + serializer +}; + +const getCardsOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "api/v1/cards/get", + queryParameters: [ + Parameters.page + ], + responses: { + 200: { + bodyMapper: Mappers.GetCardsOKResponse + }, + default: {} + }, + serializer +}; + const getProjectByAliasOperationSpec: msRest.OperationSpec = { httpMethod: "GET", path: "api/v1/alias/project/get", @@ -1903,6 +2081,21 @@ const createCardOperationSpec: msRest.OperationSpec = { serializer }; +const getCardOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "api/v1/card/get", + queryParameters: [ + Parameters.cardGuid + ], + responses: { + 200: { + bodyMapper: Mappers.GetCardOKResponse + }, + default: {} + }, + serializer +}; + const editCardOperationSpec: msRest.OperationSpec = { httpMethod: "PATCH", path: "api/v1/card/edit", @@ -2272,6 +2465,9 @@ const getMyEntityPermissionsOperationSpec: msRest.OperationSpec = { const getNewestProjectsOperationSpec: msRest.OperationSpec = { httpMethod: "GET", path: "api/v1/projects/newest/get", + queryParameters: [ + Parameters.page + ], responses: { 200: { bodyMapper: Mappers.GetNewestProjectsOKResponse @@ -2293,6 +2489,21 @@ const getRandomProjectsOperationSpec: msRest.OperationSpec = { serializer }; +const getUserProjectsOperationSpec: msRest.OperationSpec = { + httpMethod: "GET", + path: "api/v1/user/projects/get", + queryParameters: [ + Parameters.userGuid + ], + responses: { + 200: { + bodyMapper: Mappers.GetUserProjectsOKResponse + }, + default: {} + }, + serializer +}; + const getProjectProductsOperationSpec: msRest.OperationSpec = { httpMethod: "GET", path: "api/v1/project/products/get", diff --git a/src/components/action/library/AddToLibraryButton/AddToLibraryButton.tsx b/src/components/action/library/AddToLibraryButton/AddToLibraryButton.tsx new file mode 100644 index 0000000..6a55aaa --- /dev/null +++ b/src/components/action/library/AddToLibraryButton/AddToLibraryButton.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import {ProjectModel} from "../../../../client/bindings"; +import {Button, notification} from "antd"; +import {handleApiError} from "../../../../classes/notification/errorHandler/errorHandler"; + +interface IProps { + project: ProjectModel +} + +interface IState {} + +class AddToLibraryButton extends React.Component { + constructor(props: IProps) { + super(props); + this.state = {}; + } + + addToLibrary() { + if (!window.App.isAuthorized()) { + notification['warning']({ + message:
Please sign in first
+ }); + return; + } + window.App.apiClient.addProjectToMyLibrary(window.App.apiToken, this.props.project.guid!) + .then(() => { + notification['success']({ + message:
Project was added to your library
+ }); + }) + .catch((error) => handleApiError(error.response)); + } + + render() { + return
+ +
+ } +} + +export default AddToLibraryButton; diff --git a/src/components/action/support/SupportButton.tsx b/src/components/action/support/SupportButton.tsx new file mode 100644 index 0000000..57a34f6 --- /dev/null +++ b/src/components/action/support/SupportButton.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import NewInvoice from "../../entity/invoice/single/create/NewInvoice"; +import {Link} from "react-router-dom"; +import {Button, Col, Modal, Row} from "antd"; + +interface IProps { + entityName: string, + entityGuid: string, + entityType: any, +} + +interface IState { + showModal: boolean, +} + +export default class SupportButton extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + showModal: false, + } + } + + render() { + let supportLabel = `Support ${this.props.entityName}`; + + return
+ {supportLabel}} + visible={this.state.showModal} + width={window.innerWidth < 1000 ? "90%" : "50%"} + onCancel={() => { + this.setState({showModal: false}) + }} + footer={null} + > + + Pay using PayPal (Gumroad) + + Single payment + Monthly subscription + + + Pay with cryptocurrency + + + { + window.App.isAuthorized() ? + :

+ + + to pay via crypto +

+ } + +
+
+ +
+ } +} diff --git a/src/components/auth/redirect/AuthRedirect.tsx b/src/components/auth/redirect/AuthRedirect.tsx new file mode 100644 index 0000000..70edcf3 --- /dev/null +++ b/src/components/auth/redirect/AuthRedirect.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Redirect } from 'react-router'; +import {notification} from "antd"; + +class AuthRedirect extends React.Component { + componentDidMount(): void { + if (!window.App.isAuthorized()) { + notification['error']({ + message: 'You need to log in to view this page' + }); + } + } + + render() { + return !window.App.isAuthorized() ? : null; + } +} + +export default AuthRedirect; \ No newline at end of file diff --git a/src/components/custom/antd/pagination/Pagination.tsx b/src/components/custom/antd/pagination/Pagination.tsx new file mode 100644 index 0000000..9f7314b --- /dev/null +++ b/src/components/custom/antd/pagination/Pagination.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import {Pagination as AntPagination} from "antd"; + +interface IProps { + currentPage: number, + pagesCount: number, + onChange: any, +} + +interface IState { +} + +class Pagination extends React.Component { + componentDidMount(): void { + this.updateLinks(); + } + + componentDidUpdate(): void { + this.updateLinks(); + } + + updateLinks() { + let pageButtons = document.getElementsByClassName("ant-pagination-item"); + for (let i = 0; i <= pageButtons.length; i++) { + if (pageButtons[i]) { + let pageNum = pageButtons[i].getAttribute('title'); + pageButtons[i].innerHTML = `${pageNum}`; + } + } + } + + render() { + return this.props.onChange(page)} + pageSize={1} + total={this.props.pagesCount} + /> + } +} + +export default Pagination; diff --git a/src/components/entity/card/many/list/CardsList.tsx b/src/components/entity/card/many/list/CardsList.tsx new file mode 100644 index 0000000..a35ea5e --- /dev/null +++ b/src/components/entity/card/many/list/CardsList.tsx @@ -0,0 +1,94 @@ +import React from "react"; +import {handleApiError} from "../../../../../classes/notification/errorHandler/errorHandler"; +import {Col, Icon, Row} from "antd"; +import {CardModel} from "../../../../../client/bindings"; +import {Link} from "react-router-dom"; +import CardCard from "../../single/card/CardCard"; +import Pagination from "../../../../custom/antd/pagination/Pagination"; +import CardModalLoader from "../../single/modalLoader/CardModalLoader"; + +interface IProps { + dataSource: string +} + +interface IState { + currentPage: number, + pagesCount: number, + isLoaded: boolean, + cards: CardModel[]|null, + openedCardGuid: string|null +} + +class CardsList extends React.Component { + public static defaultProps = { + dataSource: "" + }; + + constructor(props: IProps) { + super(props); + this.state = { + currentPage: 1, + pagesCount: 1, + isLoaded: false, + cards: null, + openedCardGuid: null + } + } + + componentDidMount(): void { + setTimeout(() => { + let queryPage: any = new URL(window.location.href).searchParams.get('page'); + if (queryPage) { + queryPage = parseInt(queryPage) ? parseInt(queryPage) : null; + this.setState({currentPage: queryPage}); + } + let openedCardGuid : string|null = new URL(window.location.href).searchParams.get('card'); + if (openedCardGuid) this.setState({ openedCardGuid }); + this.getCards(); + }, 50); + } + + getCards(): void { + this.setState({isLoaded: false, cards: null}); + switch (this.props.dataSource) { + case "allCards": + window.App.apiClient.getCards({page: this.state.currentPage}).then((res: any) => { + let json = JSON.parse(res._response.bodyAsText); + this.setState({ + isLoaded: true, + cards: json.data.cards, + pagesCount: json.data.meta.pages_count + }) + }).catch((error) => handleApiError(error.response)); + break; + } + } + + changePage(page: number) { + this.setState({currentPage: page}); + this.getCards(); + } + + render() { + if (!this.state.isLoaded || !this.state.cards) return ; + return
+ {this.state.openedCardGuid ? : null} + + {this.state.cards != null && this.state.cards.map((card: CardModel, i: number) => { + return + + + + ; + })} + + +
+ } +} + +export default CardsList; diff --git a/src/components/entity/card/single/card/CardCard.tsx b/src/components/entity/card/single/card/CardCard.tsx index 718e96b..2584497 100644 --- a/src/components/entity/card/single/card/CardCard.tsx +++ b/src/components/entity/card/single/card/CardCard.tsx @@ -10,20 +10,26 @@ import {BoardModel, CardModel} from "../../../../../client/bindings"; import PermissionCheck from "../../../../check/permission_check/single/PermissionCheck"; import EditCard from "../../action/edit/EditCard"; import AuthCheck from "../../../../check/auth_check/AuthCheck"; +import moment from "moment"; +import ReactMarkdown from "react-markdown"; interface IProps { parentBoard: BoardModel|null, card: CardModel, + autoOpenModal: boolean, + forceOpenModal: boolean } interface IState { showModal: boolean, - modalCanceled: boolean + modalCanceled: boolean, } class CardCard extends React.Component { public static defaultProps = { - parentBoard: null + parentBoard: null, + autoOpenModal: true, + forceOpenModal: false }; constructor(props: IProps) { @@ -36,7 +42,7 @@ class CardCard extends React.Component { componentDidMount(): void { let selectedCardGuid = new URL(window.location.href).searchParams.get('card'); - if (selectedCardGuid === this.props.card.guid) { + if (this.props.forceOpenModal || (selectedCardGuid === this.props.card.guid && this.props.autoOpenModal)) { this.setState(({ showModal: true })); @@ -59,11 +65,10 @@ class CardCard extends React.Component { className={styles.root + " material-shadow-hover-1"} onClick={this.cardOnClick.bind(this)} > -

{card.name}

- -
- cards here -
+ {card.name} + + Created: {moment(card.created_at).format('MMMM Do YYYY')} + {card.name}} @@ -88,7 +93,9 @@ class CardCard extends React.Component { -

{card.description ? card.description : "no content"}

+ diff --git a/src/components/entity/card/single/modalLoader/CardModalLoader.tsx b/src/components/entity/card/single/modalLoader/CardModalLoader.tsx new file mode 100644 index 0000000..6a25d12 --- /dev/null +++ b/src/components/entity/card/single/modalLoader/CardModalLoader.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import {CardModel} from "../../../../../client/bindings"; +import CardCard from "../card/CardCard"; + +interface IProps { + cardGuid: string +} + +interface IState { + card: CardModel|null +} + +class CardModalLoader extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + card: null + } + } + + componentDidMount(): void { + this.getCard(); + } + + getCard() { + window.App.apiClient.getCard(this.props.cardGuid).then(res => { + let json = JSON.parse(res._response.bodyAsText); + this.setState({ + card: json.data.card + }); + }) + } + + render() { + if (!this.state.card) return null; + return
; + } +} + +export default CardModalLoader; diff --git a/src/components/entity/column/single/card/ColumnCard.tsx b/src/components/entity/column/single/card/ColumnCard.tsx index 8ea9ee1..e8a88aa 100644 --- a/src/components/entity/column/single/card/ColumnCard.tsx +++ b/src/components/entity/column/single/card/ColumnCard.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import {Card, Divider, Icon, Row} from "antd"; +import {Button, Card, Divider, Icon, notification, Row} from "antd"; import {BoardModel, CardModel, ColumnModel} from "../../../../../client/bindings"; import {handleApiError} from "../../../../../classes/notification/errorHandler/errorHandler"; import styles from './styles.module.css'; @@ -15,7 +15,9 @@ interface IProps { interface IState { isLoaded: boolean, - cards: CardModel[]|null + cards: CardModel[]|null, + lastPageLoaded: number, + moreAvailable: boolean } class ColumnCard extends React.Component { @@ -23,7 +25,9 @@ class ColumnCard extends React.Component { super(props); this.state = { isLoaded: false, - cards: null + cards: null, + lastPageLoaded: 0, + moreAvailable: true } } @@ -38,22 +42,25 @@ class ColumnCard extends React.Component { }, Math.floor(Math.random() * 3000)); } - getCards(): void { - window.App.apiClient.getColumnCards(this.props.column.guid!) - .then((result) => - this.processGetBoardInfo(result._response)) + getCards(page: number = 1): void { + window.App.apiClient.getColumnCards(this.props.column.guid!, { + page + }) + .then((res) => { + let rawData: any = res.data!; + if (this.state.cards === null) this.setState({cards: []}); + this.setState({ + isLoaded: true, + cards: this.state.cards!.concat(res.data!.cards!), + lastPageLoaded: rawData.meta.current_page + }); + if (this.state.lastPageLoaded >= rawData.meta.pages_count) { + this.setState({moreAvailable: false}); + } + }) .catch((error) => handleApiError(error.response)); } - processGetBoardInfo(response: any) { - let json = JSON.parse(response.bodyAsText); - - this.setState({ - isLoaded: true, - cards: json.data.cards - }); - } - render() { return ( @@ -68,6 +75,10 @@ class ColumnCard extends React.Component {
; })} + {this.state.moreAvailable ? : null} div { diff --git a/src/components/entity/my_library/many/MyLibraryItems.tsx b/src/components/entity/my_library/many/MyLibraryItems.tsx index 287f9ac..8601b5d 100644 --- a/src/components/entity/my_library/many/MyLibraryItems.tsx +++ b/src/components/entity/my_library/many/MyLibraryItems.tsx @@ -46,7 +46,7 @@ class MyLibraryItems extends React.Component { return ; } return
- + {this.state.items.length === 0 ?

Your library is empty

: null} {this.state.items.map((item: LibraryItem, i: number) => { return diff --git a/src/components/entity/my_library/single/card/ItemCard.tsx b/src/components/entity/my_library/single/card/ItemCard.tsx index 4d6c43d..ff5a9fc 100644 --- a/src/components/entity/my_library/single/card/ItemCard.tsx +++ b/src/components/entity/my_library/single/card/ItemCard.tsx @@ -67,7 +67,7 @@ class ItemCard extends React.Component { let project = this.state.project; return - + Added:

{this.props.item.created_at}

diff --git a/src/components/entity/project/many/cards_list/ProjectCardList.tsx b/src/components/entity/project/many/cards_list/ProjectCardList.tsx index 2f11110..d7d6bd7 100644 --- a/src/components/entity/project/many/cards_list/ProjectCardList.tsx +++ b/src/components/entity/project/many/cards_list/ProjectCardList.tsx @@ -1,41 +1,70 @@ import React from "react"; import {ProjectModel} from "../../../../../client/bindings"; import {handleApiError} from "../../../../../classes/notification/errorHandler/errorHandler"; -import {Col, Icon, Row} from "antd"; +import {Card, Col, Row, Skeleton} from "antd"; import ProjectCard from "../../single/card/ProjectCard"; -import {retryRequest} from "../../../../../classes/utils/http/retryRequest"; +import Pagination from "../../../../custom/antd/pagination/Pagination"; + +const { Meta } = Card; interface IProps { label: string, - type: string + type: string, + userGuid: string|null, + displayPagination: boolean, } interface IState { isLoaded: boolean, - projects: ProjectModel[]|null + projects: ProjectModel[]|null, + currentPage: number, + pagesCount: number, } class ProjectCardList extends React.Component { + public static defaultProps = { + userGuid: null, + displayPagination: false + }; + constructor(props: IProps) { super(props); this.state = { isLoaded: false, - projects: null + projects: null, + currentPage: 1, + pagesCount: 1, } } componentDidMount(): void { - retryRequest(() => { + {/* TODO: fix this hack */} + setTimeout(() => { + let queryPage: any = new URL(window.location.href).searchParams.get('page'); + if (queryPage) { + queryPage = parseInt(queryPage) ? parseInt(queryPage) : null; + this.setState({currentPage: queryPage}); + console.log(queryPage); + } this.getProjects(); - }, () => this.state.isLoaded, true); + }, 50); } getProjects() { switch (this.props.type) { case "newest": - window.App.apiClient.getNewestProjects() - .then((result) => - this.processGetProjects(result._response)) + window.App.apiClient.getNewestProjects({ + page: this.state.currentPage + }) + .then((res:any) => { + let json = JSON.parse(res._response.bodyAsText); + this.setState({ + isLoaded: true, + projects: json.data.projects, + currentPage: json.data.meta.current_page, + pagesCount: json.data.meta.pages_count + }); + }) .catch((error) => handleApiError(error.response)); break; case "random": @@ -44,6 +73,12 @@ class ProjectCardList extends React.Component { this.processGetProjects(result._response)) .catch((error) => handleApiError(error.response)); break; + case "user": + window.App.apiClient.getUserProjects(this.props.userGuid!) + .then((result) => + this.processGetProjects(result._response)) + .catch((error) => handleApiError(error.response)); + break; } } @@ -52,23 +87,48 @@ class ProjectCardList extends React.Component { this.setState({ isLoaded: true, projects: json.data.projects - }) + }); + } + + changePage(page: number) { + this.setState({currentPage: page}); } render() { - let projectsList = this.state.projects ? this.state.projects.slice(0, 9) : null; + let projectsList = this.state.projects ? this.state.projects : null; + + let loadingCards = []; + if (!this.state.isLoaded) { + for (let i = 0; i < 10; i++) { + loadingCards.push( + + + + + + ); + } + } return

{this.props.label}

- { this.state.isLoaded ? + {this.state.isLoaded ? - {projectsList && projectsList.map((project: ProjectModel, i: number) => { - return + {projectsList && projectsList.map((project: ProjectModel) => { + return ; })} : - + {loadingCards.map(cardBlock => cardBlock)} + } + { + this.props.displayPagination ? + : null }
; } diff --git a/src/components/entity/project/single/card/ProjectCard.tsx b/src/components/entity/project/single/card/ProjectCard.tsx index d126f96..40e645a 100644 --- a/src/components/entity/project/single/card/ProjectCard.tsx +++ b/src/components/entity/project/single/card/ProjectCard.tsx @@ -1,23 +1,50 @@ -import {ProjectModel} from "../../../../../client/bindings"; +import {Image, ProjectModel} from "../../../../../client/bindings"; import React from "react"; import {Link} from "react-router-dom"; import {Card} from "antd"; import ProjectInfo from "../info/ProjectInfo"; interface IProps { - project: ProjectModel + project: ProjectModel, } interface IState { + image: Image|null } class ProjectCard extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + image: null + } + } + + componentDidMount() { + setTimeout(() => { + window.App.apiClient.getProjectImages(this.props.project.guid!) + .then((res: any) => { + let images = JSON.parse(res._response.bodyAsText).data.images; + if (images.length > 0) this.setState({ + image: images[0] + }); + }); + }, 750); + } + render() { - return - - - - ; + const project = this.props.project; + + let cardImage =
; + if (this.state.image) cardImage = example; + + return {project.name}} + cover={cardImage} + > + + ; } } diff --git a/src/components/entity/project/single/info/ProjectInfo.tsx b/src/components/entity/project/single/info/ProjectInfo.tsx index bca8927..c48638b 100644 --- a/src/components/entity/project/single/info/ProjectInfo.tsx +++ b/src/components/entity/project/single/info/ProjectInfo.tsx @@ -4,8 +4,11 @@ import {handleApiError} from "../../../../../classes/notification/errorHandler/e import {Button, Icon, Row} from "antd"; import moment from "moment"; import {retryRequest} from "../../../../../classes/utils/http/retryRequest"; +import {Link} from "react-router-dom"; +import AddToLibraryButton from "../../../../action/library/AddToLibraryButton/AddToLibraryButton"; interface IProps { + displayName: boolean, guid: string|null, project: ProjectModel|null } @@ -18,6 +21,7 @@ interface IState { class ProjectInfo extends React.Component { public static defaultProps = { + displayName: false, guid: null, project: null }; @@ -58,8 +62,6 @@ class ProjectInfo extends React.Component { isLoaded: true, project: json.data.project }); - - console.log('state UPDATD!'); } render() { @@ -70,8 +72,12 @@ class ProjectInfo extends React.Component { let project : ProjectModel = this.state.project!; return
- {project.name!} -

+ { this.props.displayName ?
+ + {project.name!} + +

+
: null }
{project.description!}
@@ -79,7 +85,7 @@ class ProjectInfo extends React.Component { Created: {moment(project.created_at).format('MMMM Do YYYY')}

- +
; } diff --git a/src/components/external/external_repo/card/ExternalRepoCard.tsx b/src/components/external/external_repo/card/ExternalRepoCard.tsx index cca69cd..db8b79f 100644 --- a/src/components/external/external_repo/card/ExternalRepoCard.tsx +++ b/src/components/external/external_repo/card/ExternalRepoCard.tsx @@ -1,43 +1,105 @@ import React from "react"; -import {Button, Card, Col, Row} from "antd"; +import {Button, Card, Col, notification, Row} from "antd"; +import {ServiceType1} from "../../../../client/models"; +import {ProjectModel} from "../../../../client/bindings"; import {Link} from "react-router-dom"; -import {buildQuery} from "../../../../classes/utils/url/QueryBuilder"; interface IProps { repo: { name: string, description: string, - serviceType: string, + serviceType: ServiceType1, originId: string } } -interface IState {} +interface IState { + importButton: { + isLoading: boolean, + label: string + }, + importedProject: ProjectModel|null +} class ExternalRepoCard extends React.Component { - getImportUrl(): string { - return "/user/repository/import?" + buildQuery({ - origin_id: this.props.repo.originId, - service_type: this.props.repo.serviceType - }); + constructor(props: IProps) { + super(props); + this.state = { + importButton: { + isLoading: false, + label: "Import" + }, + importedProject: null + } } + /* TODO: get repo status and if already imported - show 'go to project' button */ + getExternalUrl(): string { let repo = this.props.repo; return `https://${repo.serviceType}.com/${repo.name}`; } + sendImportRequest() { + this.setState({ + importButton: { + label: 'Importing...', + isLoading: true + } + }); + + const repo = this.props.repo; + window.App.apiClient.postImportRepository( + window.App.apiToken, + repo.originId, + repo.serviceType + ) + .then((res) => { + const project = JSON.parse(res._response.bodyAsText).data.project; + if (!project.base_uri) return; + notification['success']({ + message: `Project '${project.name}' imported successfully!` + }); + this.setState({ + importedProject: project + }); + }) + .catch((err) => { + const project = JSON.parse(err.response.body).metadata.project; + if (!project.base_uri) return; + this.setState({ + importedProject: project + }); + }); + } + render() { + let importButton = this.state.importButton; + const importedProject = this.state.importedProject; + return {this.props.repo.name}
- Description:

description here


- Status:

unknown

+ Description:

{this.props.repo.description}


- + { + importedProject ? +
+

Project imported

+ + + +
+ : + + }
+ +
+ : null + } ; } } diff --git a/src/layouts/account/billing/BillingLayout.tsx b/src/layouts/account/billing/BillingLayout.tsx index 1cc06e9..403d759 100644 --- a/src/layouts/account/billing/BillingLayout.tsx +++ b/src/layouts/account/billing/BillingLayout.tsx @@ -5,6 +5,7 @@ import InvoiceCardsList from "../../../components/entity/invoice/many/Cards/Invo import {Row} from "antd"; import UserBalanceCard from "../../../components/entity/user_balance/single/card/UserBalanceCard"; import NewInvoice from "../../../components/entity/invoice/single/create/NewInvoice"; +import AuthRedirect from "../../../components/auth/redirect/AuthRedirect"; interface IProps { } @@ -15,6 +16,7 @@ interface IState { class BillingLayout extends React.Component { constructor(props: IProps) { + document.title = "Billing | GitCom - Community-Driven open source marketplace"; super(props); this.state = { isUserLoaded: false @@ -31,6 +33,7 @@ class BillingLayout extends React.Component { render() { return +

Billing

Account balance

diff --git a/src/layouts/account/library/LibraryLayout.tsx b/src/layouts/account/library/LibraryLayout.tsx index 3ca1b6c..4fa53fa 100644 --- a/src/layouts/account/library/LibraryLayout.tsx +++ b/src/layouts/account/library/LibraryLayout.tsx @@ -2,22 +2,20 @@ import React from 'react'; import FullPageWithSideBar from "../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; import {Row} from "antd"; import MyLibraryItems from "../../../components/entity/my_library/many/MyLibraryItems"; +import AuthRedirect from "../../../components/auth/redirect/AuthRedirect"; -interface IProps { -} - -interface IState { -} - -class LibraryLayout extends React.Component { - constructor(props: IProps) { +class LibraryLayout extends React.Component { + constructor(props: any) { + document.title = "My library | GitCom - Community-Driven open source marketplace"; super(props); this.state = {} } render() { return +

My library

+

Your monthly subscription will be distributed across those projects

diff --git a/src/layouts/account/settings/SettingsLayout.tsx b/src/layouts/account/settings/SettingsLayout.tsx index 7fbef8f..bfb3518 100644 --- a/src/layouts/account/settings/SettingsLayout.tsx +++ b/src/layouts/account/settings/SettingsLayout.tsx @@ -1,19 +1,17 @@ import React from 'react'; import FullPageWithSideBar from "../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; +import AuthRedirect from "../../../components/auth/redirect/AuthRedirect"; -interface IProps { -} - -interface IState { -} - -class SettingsLayout extends React.Component { - constructor(props: IProps) { +class SettingsLayout extends React.Component { + constructor(props: any) { + document.title = "Settings | GitCom - Community-Driven open source marketplace"; super(props); this.state = {} } + render() { return +
Settings layout
diff --git a/src/layouts/account/subscription/SubscriptionLayout.tsx b/src/layouts/account/subscription/SubscriptionLayout.tsx index 1365da6..191ea00 100644 --- a/src/layouts/account/subscription/SubscriptionLayout.tsx +++ b/src/layouts/account/subscription/SubscriptionLayout.tsx @@ -3,15 +3,12 @@ import FullPageWithSideBar from "../../../components/layout/simple/fullpagewiths import UserBalanceCard from "../../../components/entity/user_balance/single/card/UserBalanceCard"; import {Col, Row} from "antd"; import UserSetting from "../../../components/entity/user_settings/single/UserSetting"; +import {Link} from "react-router-dom"; +import AuthRedirect from "../../../components/auth/redirect/AuthRedirect"; -interface IProps { -} - -interface IState { -} - -class SubscriptionLayout extends React.Component { - constructor(props: IProps) { +class SubscriptionLayout extends React.Component { + constructor(props: any) { + document.title = "Subscription | GitCom - Community-Driven open source marketplace"; super(props); this.state = {} } @@ -19,6 +16,7 @@ class SubscriptionLayout extends React.Component { render() { return
+

Subscription settings

@@ -30,6 +28,10 @@ class SubscriptionLayout extends React.Component {

Settings

+

+ We will try to charge this amount from your balance each month, this money will be + distributed across projects that are in your library +

{ +class WithdrawalsLayout extends React.Component { render() { return +

Withdrawal requests

Account balance

diff --git a/src/layouts/app/AppLayout.tsx b/src/layouts/app/AppLayout.tsx index 82c665c..b85bd29 100644 --- a/src/layouts/app/AppLayout.tsx +++ b/src/layouts/app/AppLayout.tsx @@ -38,7 +38,7 @@ class AppLayout extends React.Component { mode="horizontal" style={{lineHeight: '64px'}} > - Explore + Explore {loginOrHomeLink} Help { diff --git a/src/layouts/auth/confirmEmail/ConfirmEmailLayout.tsx b/src/layouts/auth/confirmEmail/ConfirmEmailLayout.tsx index 4dbc69e..f76e1d1 100644 --- a/src/layouts/auth/confirmEmail/ConfirmEmailLayout.tsx +++ b/src/layouts/auth/confirmEmail/ConfirmEmailLayout.tsx @@ -20,6 +20,7 @@ interface IState { class ConfirmEmailLayout extends React.Component { constructor(props: IProps) { + document.title = "Confirm email | GitCom - Community-Driven open source marketplace"; super(props); this.state = { redirectHome: false, diff --git a/src/layouts/auth/external/github/loginWith/LoginWithGitHubLayout.tsx b/src/layouts/auth/external/github/loginWith/LoginWithGitHubLayout.tsx index 557d84c..31a6ae0 100644 --- a/src/layouts/auth/external/github/loginWith/LoginWithGitHubLayout.tsx +++ b/src/layouts/auth/external/github/loginWith/LoginWithGitHubLayout.tsx @@ -57,7 +57,7 @@ class LoginWithGitHubLayout extends React.Component { }); setTimeout(() => { this.setState({ - redirectBlock: + redirectBlock: }); }, 1000); } diff --git a/src/layouts/auth/external/gitlab/logInWith/LoginWithGitLabLayout.tsx b/src/layouts/auth/external/gitlab/logInWith/LoginWithGitLabLayout.tsx index 7e3b55b..1853195 100644 --- a/src/layouts/auth/external/gitlab/logInWith/LoginWithGitLabLayout.tsx +++ b/src/layouts/auth/external/gitlab/logInWith/LoginWithGitLabLayout.tsx @@ -57,7 +57,7 @@ class LoginWithGitLabLayout extends React.Component { }); setTimeout(() => { this.setState({ - redirectBlock: + redirectBlock: }); }, 1000); } diff --git a/src/layouts/auth/login/LoginLayout.tsx b/src/layouts/auth/login/LoginLayout.tsx index 2b29c72..c0eb9a8 100644 --- a/src/layouts/auth/login/LoginLayout.tsx +++ b/src/layouts/auth/login/LoginLayout.tsx @@ -26,6 +26,7 @@ interface IState { class LoginLayout extends React.Component { constructor(props: any) { + document.title = "Sign in | GitCom - Community-Driven open source marketplace"; super(props); this.state = { email: "", diff --git a/src/layouts/auth/register/RegisterLayout.tsx b/src/layouts/auth/register/RegisterLayout.tsx index e1c3c49..3081445 100644 --- a/src/layouts/auth/register/RegisterLayout.tsx +++ b/src/layouts/auth/register/RegisterLayout.tsx @@ -21,6 +21,7 @@ interface IState { class RegisterLayout extends React.Component { constructor(props: any) { + document.title = "Register | GitCom - Community-Driven open source marketplace"; super(props); this.state = { registrationComplete: false, diff --git a/src/layouts/entity/board/page/BoardPage.tsx b/src/layouts/entity/board/page/BoardPage.tsx index 9135435..9bce88d 100644 --- a/src/layouts/entity/board/page/BoardPage.tsx +++ b/src/layouts/entity/board/page/BoardPage.tsx @@ -92,7 +92,7 @@ class BoardPage extends React.Component { let board = this.state.board; - return + return {breadCrumb}

{board.name}

{board.description}

diff --git a/src/layouts/entity/project/pricing/edit/EditProjectPricingLayout.tsx b/src/layouts/entity/project/pricing/edit/EditProjectPricingLayout.tsx index b173899..94017fd 100644 --- a/src/layouts/entity/project/pricing/edit/EditProjectPricingLayout.tsx +++ b/src/layouts/entity/project/pricing/edit/EditProjectPricingLayout.tsx @@ -38,20 +38,17 @@ class EditProjectPricingLayout extends React.Component { getProjectInfo(): void { window.App.apiClient.getProjectByAlias(this.props.match.params.owner, this.props.match.params.alias) - .then((result) => - this.processGetProjectInfo(result._response)) - .catch((error) => handleApiError(error.response)); - } - - processGetProjectInfo(response: any) { - let json = JSON.parse(response.bodyAsText); + .then((res) => { + let json = JSON.parse(res._response.bodyAsText); - this.setState({ - isLoaded: true, - project: json.data.project - }); + this.setState({ + isLoaded: true, + project: json.data.project + }); - this.getProducts(); + this.getProducts(); + }) + .catch((error) => handleApiError(error.response)); } getProducts() { diff --git a/src/layouts/entity/project/sub_pages/cards/ProjectCardsLayout.tsx b/src/layouts/entity/project/sub_pages/cards/ProjectCardsLayout.tsx new file mode 100644 index 0000000..6962390 --- /dev/null +++ b/src/layouts/entity/project/sub_pages/cards/ProjectCardsLayout.tsx @@ -0,0 +1,132 @@ +import React from "react"; +import FullPageWithSideBar from "../../../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; +import {Card, Col, Icon, Row, Skeleton} from "antd"; +import {CardModel, ProjectModel} from "../../../../../client/bindings"; +import {handleApiError} from "../../../../../classes/notification/errorHandler/errorHandler"; +import CardCard from "../../../../../components/entity/card/single/card/CardCard"; +import {Link} from "react-router-dom"; +import Pagination from "../../../../../components/custom/antd/pagination/Pagination"; +import CardModalLoader from "../../../../../components/entity/card/single/modalLoader/CardModalLoader"; + +const { Meta } = Card; + +interface IProps { + match: { + params: { + owner: string, + alias: string + } + } +} + +interface IState { + isLoaded: boolean, + project: ProjectModel|null, + cards: CardModel[]|null, + pagesCount: number, + currentPage: number, + openedCardGuid: string|null +} + +class ProjectCardsLayout extends React.Component { + constructor(props: IProps) { + super(props); + this.state = { + isLoaded: false, + project: null, + cards: null, + pagesCount: 1, + currentPage: 1, + openedCardGuid: null + }; + } + + componentDidMount(): void { + let queryPage : any = new URL(window.location.href).searchParams.get('page'); + if (queryPage) { + queryPage = parseInt(queryPage) ? parseInt(queryPage) : null; + this.setState({currentPage: queryPage}); + } + let openedCardGuid : string|null = new URL(window.location.href).searchParams.get('card'); + if (openedCardGuid) this.setState({ openedCardGuid }); + this.getProjectInfo(); + } + + getProjectInfo(): void { + window.App.apiClient.getProjectByAlias(this.props.match.params.owner, this.props.match.params.alias) + .then((res) => { + this.setState({ + isLoaded: true, + project: res.data!.project! + }); + + this.getCards(this.state.currentPage); + }) + .catch((error) => handleApiError(error.response)); + } + + getCards(page: number = 1): void { + this.setState({cards: null}); + window.App.apiClient.getProjectCards(this.state.project!.guid!, { + page + }).then((res) => { + let rawData: any = res.data!; + this.setState({ + cards: res.data!.cards!, + currentPage: rawData.meta.current_page, + pagesCount: rawData.meta.pages_count + }); + }); + } + + render() { + if (!this.state.isLoaded) { + return +

Loading cards...

+ +
; + } + + let project = this.state.project; + + let skeletons = []; + + if (this.state.cards === null) { + for (let i = 0; i < 10; i++) { + skeletons.push( + + + + + + ); + } + } + + let cards = this.state.cards; + + let pagesCount = this.state.pagesCount; + + return +

{project!.name}

+ {this.state.openedCardGuid ? : null} + + {cards ? this.state.cards!.map((card: CardModel, i: number) => { + return + + + + + }) : skeletons} + {pagesCount ? + + : null} +
+ } +} + +export default ProjectCardsLayout; diff --git a/src/layouts/entity/project/view/ProjectPage.tsx b/src/layouts/entity/project/view/ProjectPage.tsx index ca52f9c..36ddbe4 100644 --- a/src/layouts/entity/project/view/ProjectPage.tsx +++ b/src/layouts/entity/project/view/ProjectPage.tsx @@ -1,7 +1,7 @@ import React from "react"; import FullPageWithSideBar from "../../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; import {handleApiError} from "../../../../classes/notification/errorHandler/errorHandler"; -import {Button, Card, Col, Divider, Icon, Row} from "antd"; +import {Badge, Button, Card, Col, Divider, Icon, Popover, Row} from "antd"; import {BoardModel, ProjectModel} from "../../../../client/bindings"; import BoardCard from "../../../../components/entity/board/single/card/BoardCart"; import PermissionCheckLink from "../../../../components/link/withChecks/PermissionCheckLink"; @@ -11,6 +11,7 @@ import RepoCard from "../../../../components/external/repo/card/RepoCard"; import {Link} from "react-router-dom"; import NewInvoice from "../../../../components/entity/invoice/single/create/NewInvoice"; import ProjectPosts from "../../../../components/entity/project_post/many/ProjectPosts"; +import SupportButton from "../../../../components/action/support/SupportButton"; interface IProps { match: { @@ -89,8 +90,22 @@ class ProjectPage extends React.Component { let project: ProjectModel = this.state.project; return -

{project.name}

+

{project.name}

{project.description}

+ { project.confirmed ? null : + + You can react out to project owners and ask them to sign up and confirm this project.
+ If you're owner of this project you can sign in to + confirm ownership or remove the project +

}> + +
+
}
@@ -109,20 +124,11 @@ class ProjectPage extends React.Component { />
- { - window.App.isAuthorized() ? - :

- - - to support this project -

- } +
diff --git a/src/layouts/explore/cards/ExploreCardsLayout.tsx b/src/layouts/explore/cards/ExploreCardsLayout.tsx new file mode 100644 index 0000000..1b7d22e --- /dev/null +++ b/src/layouts/explore/cards/ExploreCardsLayout.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import FullPageWithSideBar from "../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; +import CardsList from "../../../components/entity/card/many/list/CardsList"; + +class ExploreCardsLayout extends React.Component { + render() { + return +

Explore cards

+ +
+ } +} + +export default ExploreCardsLayout; \ No newline at end of file diff --git a/src/layouts/explore/projects/ExploreProjectsLayout.tsx b/src/layouts/explore/projects/ExploreProjectsLayout.tsx new file mode 100644 index 0000000..e3890d0 --- /dev/null +++ b/src/layouts/explore/projects/ExploreProjectsLayout.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import FullPageWithSideBar from "../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; +import ProjectCardList from "../../../components/entity/project/many/cards_list/ProjectCardList"; + +class ExploreProjectsLayout extends React.Component { + render() { + return + + + } +} + +export default ExploreProjectsLayout; diff --git a/src/layouts/home/integrations/HomeIntegrationsLayout.tsx b/src/layouts/home/integrations/HomeIntegrationsLayout.tsx index f98c274..893dd10 100644 --- a/src/layouts/home/integrations/HomeIntegrationsLayout.tsx +++ b/src/layouts/home/integrations/HomeIntegrationsLayout.tsx @@ -4,6 +4,7 @@ import {Button, Divider, Icon, Row} from "antd"; import {Link} from "react-router-dom"; import {handleApiError} from "../../../classes/notification/errorHandler/errorHandler"; import ConnectButton from "../../../components/external/auth/connectButton/ConnectButton"; +import AuthRedirect from "../../../components/auth/redirect/AuthRedirect"; interface IProps { } @@ -101,6 +102,7 @@ class HomeIntegrationsLayout extends React.Component { return
+

Integrations

diff --git a/src/layouts/home/main/HomeMainLayout.tsx b/src/layouts/home/main/HomeMainLayout.tsx index c15ee5b..10cc0f6 100644 --- a/src/layouts/home/main/HomeMainLayout.tsx +++ b/src/layouts/home/main/HomeMainLayout.tsx @@ -1,6 +1,7 @@ import React from 'react'; import FullPageWithSideBar from "../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; import {Link} from "react-router-dom"; +import AuthRedirect from "../../../components/auth/redirect/AuthRedirect"; interface IProps { } @@ -11,11 +12,13 @@ interface IState { class HomeMainLayout extends React.Component { constructor(props: IProps) { super(props); + document.title = "Home | GitCom - Community-Driven open source marketplace"; } render() { return
+

Home layout

Welcome to the GitCom platform
diff --git a/src/layouts/import/project/ProjectImportLayout.tsx b/src/layouts/import/project/ProjectImportLayout.tsx deleted file mode 100644 index 245fe33..0000000 --- a/src/layouts/import/project/ProjectImportLayout.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import React from "react"; -import FullPageWithSideBar from "../../../components/layout/simple/fullpagewithsidebar/FullPageWithSidebar"; -import {handleApiError, showUnknownError} from "../../../classes/notification/errorHandler/errorHandler"; -import {Icon} from "antd"; -import { Redirect } from "react-router"; -const queryString = require('query-string'); - -interface IProps { -} - -interface IState { - isLoaded: boolean, - alreadyImported: boolean, - isImported: boolean, - importedProjectGuid: string|null, - importedProjectUri: string|null, - redirectBlock: any -} - -class ProjectImportLayout extends React.Component { - constructor(props: IProps) { - super(props); - this.state = { - isLoaded: false, - isImported: false, - alreadyImported: false, - importedProjectGuid: null, - importedProjectUri: null, - redirectBlock: null - }; - } - - componentWillUnmount(): void { - this.setState({ - redirectBlock: null - }); - } - - componentDidMount(): void { - this.sendImportRequest(); - } - - sendImportRequest() { - let queryParams = queryString.parseUrl(window.location.href).query; - - window.App.apiClient.postImportRepository( - window.App.apiToken, - queryParams.origin_id, - queryParams.service_type - ) - .then((result) => - this.processImportResponse(result._response)) - .catch((error) => this.handleImportApiError(error.response)); - } - - handleImportApiError(response: any) { - if (response === undefined) { - return; - } - - let json = JSON.parse(response.body); - - let error = json.errors[0]; - - if (error.message === "Project is already imported") { - this.setState({ - isLoaded: true, - alreadyImported: true, - importedProjectUri: json.metadata.project.base_uri - }); - this.redirectToImportedProject(); - return; - } - - handleApiError(response); - } - - processImportResponse(response: any) { - let json = JSON.parse(response.bodyAsText); - - if (json.data.project) { - this.setState({ - isImported: true, - isLoaded: true, - importedProjectUri: json.data.project.base_uri - }); - this.redirectToImportedProject(); - } else { - showUnknownError(); - } - } - - redirectToImportedProject() { - setTimeout(() => { - this.setState({ - redirectBlock: - }); - }, 2000); - } - - getContent() { - let result = null; - - if (!this.state.isLoaded) { - return
-

Importing your project

- -
; - } - - if (this.state.alreadyImported && this.state.importedProjectGuid !== "") { - return
-

Project is already imported, redirecting to project page

- -
; - } - - if (this.state.isImported) { - return
-

Project is imported! Redirecting to project page

- -
; - } - - return result; - } - - render() { - return - {this.getContent()} - {this.state.redirectBlock} - ; - } -} - -export default ProjectImportLayout; diff --git a/src/layouts/index/IndexLayout.tsx b/src/layouts/index/IndexLayout.tsx index 1c023f3..48e105a 100644 --- a/src/layouts/index/IndexLayout.tsx +++ b/src/layouts/index/IndexLayout.tsx @@ -1,121 +1,160 @@ -import React from 'react'; +import React, { lazy } from 'react'; +import {Link} from "react-router-dom"; +import {Button, Card, Col, Divider, Icon, Row, Typography} from "antd"; import FullContainerPage from "../../components/layout/simple/fullpage/FullContainerPage"; -import ProjectCardList from "../../components/entity/project/many/cards_list/ProjectCardList"; -import {Button, Card, Col, Divider, Row, Typography} from "antd"; import FastAuth from "../../components/auth/block/FastAuth/FastAuth"; -import {Link} from "react-router-dom"; -import ProjectPosts from "../../components/entity/project_post/many/ProjectPosts"; +const ProjectCardList = lazy(() => import("../../components/entity/project/many/cards_list/ProjectCardList")); +const ProjectPosts = lazy(() => import("../../components/entity/project_post/many/ProjectPosts")); const { Title } = Typography; -class IndexLayout extends React.Component { - render() { - return ( - - - - - +function IndexLayout () { + return ( + + + { + !window.App.isAuthorized() ?
-

GitCom

-

Community-Driven open source marketplace

-

- GitCom helps users easily support open source and get rewards in return and - helping developers to get financial support for their open source work -

- - - -
- - - - Sign in and join our growing community! -

It's just a few clicks away!

-
- -
-
- -
- -

How it works?

-
- - -

For users:

- - - {"robot -

1. Sign up

- - - {"sparkling -

2. Select projects that you'd like and pay any amount each month

- - - {"hand -

3. Support open source and get rewards in return

- -
- - - - - -

For developers:

- - - {"robot -

1. Sign up

- - - {"sparkling -

2. Import your projects and set up pricing plans

- - - {"money -

3. Start earning with open source

- -
- - - + + +

GitCom

+

Support open source and get rewards in return

+

+ GitCom helps users easily support open source and get rewards in + return and + helping developers to get financial support for their open source + work +

+ + + + + + + + +

Some numbers so far

+ + +

+ 500+ projects +

+ + +

+ 20.000+ tickets +

+ +
+
+ + + + Sign in and join our growing community! +

It's just a few clicks away!

+
+ +
+
+ +
+ +

How it works?

+
+ + +

For users:

+ + + {"robot +

1. Sign up

+ + + {"sparkling +

2. Select projects that you'd like and pay any + amount + each month

+ + + {"hand +

3. Support open source and get rewards in + return

+ +
+ + + + + +

For developers:

+ + + {"robot +

1. Sign up

+ + + {"sparkling +

2. Import your projects and set up pricing + plans

+ + + {"money +

3. Start earning with open source

+ +
+ + + + + +
:
+ +

Explore

+

Check out new projects and latest updates

+
+
+ } + + + + + + + + - + + +

Latest updates

+ + +
- - - - - - - - -

Latest updates

- - -
- - ); - } + + ); } export default IndexLayout; \ No newline at end of file diff --git a/src/layouts/quickstart/developer/DeveloperQuickstartLayout.tsx b/src/layouts/quickstart/developer/DeveloperQuickstartLayout.tsx index a3dbbb9..37ed6e1 100644 --- a/src/layouts/quickstart/developer/DeveloperQuickstartLayout.tsx +++ b/src/layouts/quickstart/developer/DeveloperQuickstartLayout.tsx @@ -3,6 +3,11 @@ import {Button, Card, Col, Icon, Row} from "antd"; import FullContainerPage from "../../../components/layout/simple/fullpage/FullContainerPage"; class DeveloperQuickstartLayout extends React.Component { + constructor(props: any) { + document.title = "Developer quickstart | GitCom - Community-Driven open source marketplace"; + super(props); + } + render() { return diff --git a/src/layouts/repos/external/default/ExternalRepoLayout.tsx b/src/layouts/repos/external/default/ExternalRepoLayout.tsx index 1d62239..8b01f71 100644 --- a/src/layouts/repos/external/default/ExternalRepoLayout.tsx +++ b/src/layouts/repos/external/default/ExternalRepoLayout.tsx @@ -73,7 +73,7 @@ class ExternalRepoLayout extends React.Component { {!this.state.isLoaded ? : null} - + {this.state.repositories.map((item: any, i: number) => { return