+
{ r.setValue(e.target.value) }}
+ className='
+ mt-5
+ form-control
+ block
+ w-full
+ pr-11
+ pl-3
+ py-1.5
+ text-black
+ font-normal
+ bg-white bg-clip-padding
+ border border-solid border-light
+ rounded-lg
+ transition
+ ease-in-out
+ m-0
+ focus:text-gray-700 focus:bg-white focus:border-setlife focus:outline-none
+ '
+ />
+
pasteFromClipboard(r)}>
+
+
+
+ )
+ })
+ }
+
+ const handleSaveNodeButton = async () => {
+ try {
+ const response = await axios.post(`${API_ROOT}/connect`, { host, port, macaroon })
+ if (response.data.block_hash) {
+ setInvalidNode(false)
+ const variables = {
+ host: host,
+ port: Number(port),
+ macaroon: macaroon
+ }
+ await updateNode({ variables: variables })
+ } else {
+ setInvalidNode(true)
+ setDisplayAlert(true)
+ }
+ } catch (error) {
+ setInvalidNode(true)
+ setDisplayAlert(true)
+ }
+ }
+
+ const handleAlertClose = (event, reason) => {
+ if (reason === 'clickaway') {
+ return
+ }
+ setDisplayAlert(false)
+ }
+
+ const openOnboardingOverlay = () => {
+ setOnboardingScreenIndex(0)
+ setOnboardOverlayOpen(!onboardOverlayOpen)
+ }
+
+ return (
+
+ )
+ })
+ }
+ return (
+
+
Advanced Option
+
+
+ To receive payments in Trinary, you will need to connect a Lightning node to use it as a wallet (only LND is supported currently).
+
+
+ {renderSetupSteps()}
+
+ )
+ }
+
+ const renderSecond = () => {
+ return (
+
+
+
Set up a Lightning node
+
+
+
+ Set up a node using the platform of your preference.
+
+
+
+
+ )
+ }
+
+ const renderThird = () => {
+ return (
+
+
+
Provide your node information
+
+
+ To validate and register your node we will need te following information:
+
+
+ - REST Host
+ - REST Port
+ - Admin Macaroon
+
+
+
+ )
+ }
+
+ const renderFourth = () => {
+ return (
+
+
+
Receive payments directly to your node
+
+ )
+ }
+
+ const renderOnboardingScreens = () => {
+ return onboardingScreenRenders[onboardingScreenIndex]
+ }
+
+ const onboardingScreenRenders = [renderFirst(), renderSecond(), renderThird(), renderFourth()]
+
+ return (
+
+ {renderOnboardingScreens()}
+
+
+
+
+ )
+}
+
+export default AdvancedWalletSetupOnboarding
\ No newline at end of file
diff --git a/srcv2/components/AllocationTile.js b/srcv2/components/AllocationTile.js
new file mode 100644
index 00000000..fb701245
--- /dev/null
+++ b/srcv2/components/AllocationTile.js
@@ -0,0 +1,122 @@
+import React, { useState } from 'react'
+import {
+ Icon
+} from '@material-ui/core'
+import { useMutation } from '@apollo/client'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { GET_CONTRIBUTOR_ALLOCATIONS } from '../operations/queries/ContributorQueries'
+import { UPDATE_ALLOCATION } from '../operations/mutations/AllocationMutations'
+
+const AllocationTile = (props) => {
+
+ const {
+ allocation
+ } = props
+
+ const [allocationOptionsOpen, setAllocationOptionsOpen] = useState(false)
+
+ const [
+ updateAllocation, {
+ dataUpdatedAllocation,
+ loadingUpdatedAllocation,
+ errorUpdatedAllocation
+ }] = useMutation(UPDATE_ALLOCATION,
+ {
+ refetchQueries: [{
+ query: GET_CONTRIBUTOR_ALLOCATIONS,
+ variables: {
+ id: Number(sessionUser().id)
+ }
+ }]
+ })
+
+ const acceptAllocation = async () => {
+ await updateAllocation({
+ variables: {
+ id: allocation.id,
+ status: 'accepted'
+ }
+ })
+ setAllocationOptionsOpen(false)
+ }
+
+ const declineAllocation = async () => {
+ await updateAllocation({
+ variables: {
+ id: allocation.id,
+ status: 'rejected'
+ }
+ })
+ setAllocationOptionsOpen(false)
+ }
+
+ const renderAllocationOptions = () => {
+ return (
+
+
+
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ {allocation.project.name}
+
+ {allocation.status == 'pending' &&
+
+ }
+
+ {allocationOptionsOpen &&
+ renderAllocationOptions()
+ }
+ {allocation.proposedBy &&
+
+
+ {`@${allocation.proposedBy.name}`}
+
+
+ }
+
+
+
+
+ {`$ ${allocation.amount}`}
+
+
+
+
+ )
+}
+
+export default AllocationTile
\ No newline at end of file
diff --git a/srcv2/components/AllocationsList.js b/srcv2/components/AllocationsList.js
new file mode 100644
index 00000000..626a7b0b
--- /dev/null
+++ b/srcv2/components/AllocationsList.js
@@ -0,0 +1,75 @@
+import React from 'react'
+import {
+ Icon
+} from '@material-ui/core'
+import { useQuery } from '@apollo/client'
+
+import AllocationTile from './AllocationTile'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { GET_CONTRIBUTOR_ALLOCATIONS } from '../operations/queries/ContributorQueries'
+
+const AllocationsList = () => {
+
+ const allocationPrototypeLink = 'https://www.figma.com/proto/qgGWXmprU7vTv7guzWzvML/Project-Trinary?node-id=4830%3A20523&scaling=scale-down&page-id=4076%3A12706&starting-point-node-id=4830%3A20523&show-proto-sidebar=1'
+
+ const {
+ data: dataContributorAllocations,
+ loading: loadingContributorAllocations,
+ error: errorContributorAllocations
+ } = useQuery(GET_CONTRIBUTOR_ALLOCATIONS, {
+ variables: {
+ id: Number(sessionUser().id)
+ }
+ })
+
+ const renderAllocations = () => {
+ return dataContributorAllocations.getContributorById.allocations.map(allocation => {
+ return (
+
+ )
+ })
+ }
+
+ return (
+
+
+
+
+
+
+ Recent allocations
+
+
+ {loadingContributorAllocations &&
+ `Loading...`
+ }
+ {/* {(dataContributorAllocations && !dataContributorAllocations.getContributorById.allocations.length) &&
+
+
+
+
+
+
+ You haven't received any allocations yet
+
+
+
+ } */}
+
+
window.open(allocationPrototypeLink, '_blank')}>
+ Learn More
+
+
+ {dataContributorAllocations &&
+ renderAllocations()
+ }
+
+
+ )
+}
+
+export default AllocationsList
\ No newline at end of file
diff --git a/srcv2/components/Authentication.js b/srcv2/components/Authentication.js
new file mode 100644
index 00000000..6c6e572a
--- /dev/null
+++ b/srcv2/components/Authentication.js
@@ -0,0 +1,33 @@
+import React, { useEffect } from 'react'
+import { useQuery } from '@apollo/client';
+
+import { authUser, sessionUser } from '../reactivities/variables'
+import { CHECK_SESSION } from '../operations/queries/ContributorQueries'
+
+const Authentication = () => {
+
+ const { error, loading, data } = useQuery(CHECK_SESSION)
+
+ useEffect(() => {
+ if (data) {
+ if (!data.checkSession) {
+ authUser(false)
+ } else {
+ console.log('{ ...data.checkSession }');
+ console.log({ ...data.checkSession });
+ sessionUser({ ...data.checkSession })
+ authUser(true)
+ }
+ }
+ }, [data])
+
+ if (loading) return Loading...
+
+ if (error) return `Error! ${error.message}`
+ return (
+ <>
+ >
+ )
+}
+
+export default Authentication
diff --git a/srcv2/components/BtcInvoiceModal.js b/srcv2/components/BtcInvoiceModal.js
new file mode 100644
index 00000000..bc1afea3
--- /dev/null
+++ b/srcv2/components/BtcInvoiceModal.js
@@ -0,0 +1,23 @@
+import React from 'react'
+import { Modal, Box } from '@material-ui/core'
+
+export default function BtcInvoiceModal(props) {
+ const modalStyle = {
+ position: 'absolute',
+ top: '50%',
+ left: '50%',
+ transform: 'translate(-50%, -50%)',
+ }
+ return (
+ <>
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/srcv2/components/BudgetingOnboarding.js b/srcv2/components/BudgetingOnboarding.js
new file mode 100644
index 00000000..1d9373d3
--- /dev/null
+++ b/srcv2/components/BudgetingOnboarding.js
@@ -0,0 +1,46 @@
+import React from 'react'
+
+import Section from './Section'
+import OnboardingNextSection from './OnboardingNextSection'
+
+import {
+ BUDGETING_IMAGE_URL
+} from '../constants'
+
+const BudgetingOnboarding = (props) => {
+
+ const {
+ goToNextSection
+ } = props
+
+ return (
+
+
+
+
+
+
+ Budgeting
+
+
+ Allocate funds to your team
+
+
+ Negotiate compensation rates
+
+
+
+
![budget]({BUDGETING_IMAGE_URL})
+
+
+
+
+
+ )
+}
+
+export default BudgetingOnboarding
\ No newline at end of file
diff --git a/srcv2/components/CreatePaymentFloatingBadge.js b/srcv2/components/CreatePaymentFloatingBadge.js
new file mode 100644
index 00000000..f731bfea
--- /dev/null
+++ b/srcv2/components/CreatePaymentFloatingBadge.js
@@ -0,0 +1,23 @@
+import React from 'react'
+import { Icon } from '@material-ui/core'
+import { useHistory } from 'react-router-dom'
+
+const CreatePaymentFloatingBadge = (props) => {
+ const {
+ projectId
+ } = props
+
+ const history = useHistory()
+
+ return (
+
+ )
+}
+
+export default CreatePaymentFloatingBadge
\ No newline at end of file
diff --git a/srcv2/components/CreateProject.js b/srcv2/components/CreateProject.js
new file mode 100644
index 00000000..1b00bd9b
--- /dev/null
+++ b/srcv2/components/CreateProject.js
@@ -0,0 +1,154 @@
+import React, { useState, useEffect } from 'react'
+import { useLazyQuery, useQuery, useMutation } from '@apollo/client'
+
+import Section from './Section'
+import Selector from './Selector'
+
+import { GET_CONTRIBUTOR_ORGANIZATIONS_FROM_GITHUB, GET_CONTRIBUTOR_REPOS_FROM_GITHUB } from '../operations/queries/ContributorQueries'
+
+const CreateProject = (props) => {
+
+ const [openUsers, setOpenUsers] = useState(false)
+ const [openRepos, setOpenRepos] = useState(false)
+ const [userGitHubOrgs, setUserGitHubOrgs] = useState([])
+ const [repoOptions, setRepoOptions] = useState([])
+ const [hasMoreRepos, setHasMoreRepos] = useState(false)
+ const [githubPage, setGithubPage] = useState(0)
+
+ const {
+ selectedUser,
+ selectedRepo,
+ setSelectedUser,
+ setSelectedRepo
+ } = props
+
+ const {
+ error: errorOrganizations,
+ data: dataOrganizations,
+ loading: loadingOrganizations
+ } = useQuery(GET_CONTRIBUTOR_ORGANIZATIONS_FROM_GITHUB, {
+ onCompleted: dataOrganizations => {
+ const githubOrganizations = dataOrganizations.getGithubOrganizations
+ setUserGitHubOrgs(githubOrganizations)
+ }
+ })
+
+ const [getRepos, {
+ error: errorOrganizationRepos,
+ data: dataOrganizationRepos,
+ loading: loadingOrganizationRepos
+ }] = useLazyQuery(GET_CONTRIBUTOR_REPOS_FROM_GITHUB, {
+ onCompleted: payload => {
+ const {
+ getGithubRepos: { length }
+ } = payload
+ if (length) {
+ setSelectedRepo(dataOrganizationRepos.getGithubRepos[0])
+ payload.getGithubRepos.forEach((repo) => {
+ if (!repoOptions.includes(repo)) {
+ setRepoOptions([
+ ...repoOptions,
+ ...payload.getGithubRepos
+ ])
+ }
+ })
+ }
+ if (length >= 99) {
+ setHasMoreRepos(true)
+ } else {
+ setHasMoreRepos(false)
+ }
+ }
+ })
+
+ useEffect(() => {
+ if (dataOrganizations && selectedUser) {
+ setRepoOptions([])
+ getRepos({
+ variables: {
+ accountId: selectedUser.id,
+ githubPageNumber: githubPage
+ }
+ })
+ }
+ }, [selectedUser])
+
+ const renderGitHubOrgs = () => {
+ const optionClick = (org) => {
+ setOpenUsers(false)
+ setSelectedUser(org)
+ }
+ return userGitHubOrgs.map(org => {
+ return (
+
+ )
+ })
+ }
+
+ const renderRepos = () => {
+ const optionClick = (repo) => {
+ setOpenRepos(false)
+ setSelectedRepo(repo)
+ }
+ return repoOptions.map(repo => {
+ return (
+
+ )
+ })
+ }
+
+ return (
+
+
+
+
+
+ Connect a GitHub project
+
+
+
+
+
+ )
+}
+
+export default CreateProject
\ No newline at end of file
diff --git a/srcv2/components/CreateProjectFloatingButton.js b/srcv2/components/CreateProjectFloatingButton.js
new file mode 100644
index 00000000..a9a4db33
--- /dev/null
+++ b/srcv2/components/CreateProjectFloatingButton.js
@@ -0,0 +1,21 @@
+import React from 'react'
+import { useHistory } from 'react-router-dom'
+
+const CreateProjectFloatingButton = () => {
+
+ const history = useHistory()
+
+ return (
+
+ )
+}
+
+export default CreateProjectFloatingButton
\ No newline at end of file
diff --git a/srcv2/components/CreateProjectFunding.js b/srcv2/components/CreateProjectFunding.js
new file mode 100644
index 00000000..8aea072b
--- /dev/null
+++ b/srcv2/components/CreateProjectFunding.js
@@ -0,0 +1,120 @@
+import React, { useState } from 'react'
+
+import Selector from './Selector'
+import Section from './Section'
+
+import { CURRENCIES, FUNDING_PLAN_TIMEFRAME_AMOUNTS } from '../constants'
+
+const CreateProjectFunding = (props) => {
+
+ const {
+ currency,
+ setBudgetRange,
+ timeframeAmount,
+ setCurrency,
+ setTimeFrameAmount
+ } = props
+
+ const [openCurrencyOpts, setOpenCurrencyOpts] = useState(false)
+ const [openTimeFrameOpts, setOpenTimeFrameOpts] = useState(false)
+
+ const renderCurrencies = () => {
+ const optionClick = (currency) => {
+ setOpenCurrencyOpts(false)
+ setCurrency(currency)
+ }
+ return CURRENCIES.map(currency => {
+ return (
+
+ )
+ })
+ }
+
+ const renderTimeFrameOpts = () => {
+ const optionClick = (timeframe) => {
+ setOpenTimeFrameOpts(false)
+ setTimeFrameAmount(timeframe)
+ }
+ return FUNDING_PLAN_TIMEFRAME_AMOUNTS.map(timeframe => {
+ return (
+
+ )
+ })
+ }
+
+ return (
+
+
+
+
+
+ Establish budget
+
+
+
+ setBudgetRange(parseInt(e.target.value, 10))}
+ className='
+ form-control
+ block
+ w-full
+ px-3
+ py-1.5
+ text-black
+ font-normal
+ bg-white bg-clip-padding
+ border border-solid border-light
+ rounded-lg
+ transition
+ ease-in-out
+ m-0
+ focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none
+ '
+ />
+
+
+
+
+
+ )
+}
+
+export default CreateProjectFunding
\ No newline at end of file
diff --git a/srcv2/components/CreateProjectOnboarding.js b/srcv2/components/CreateProjectOnboarding.js
new file mode 100644
index 00000000..5fc43495
--- /dev/null
+++ b/srcv2/components/CreateProjectOnboarding.js
@@ -0,0 +1,107 @@
+import React, { useState } from 'react'
+import { useMutation } from '@apollo/client'
+
+import Section from './Section'
+import OnboardingNextSection from './OnboardingNextSection'
+import CreateProject from './CreateProject'
+
+import { CREATE_PERMISSION } from '../operations/mutations/PermissionMutations'
+
+import { ADD_PROJECT } from '../operations/mutations/ProjectMutations'
+
+import { sessionUser } from '../reactivities/variables'
+
+const CreateProjectOnboarding = (props) => {
+
+ const {
+ goToNextSection,
+ setProjectCreated
+ } = props
+
+ const [selectedUser, setSelectedUser] = useState(null)
+ const [selectedRepo, setSelectedRepo] = useState(null)
+
+ const [
+ addProject,
+ {
+ data,
+ loading: loadingAddProject,
+ error
+ }
+ ] = useMutation(ADD_PROJECT, {
+ errorPolicy: 'all'
+ })
+
+ const [
+ createPermission,
+ {
+ data: dataNewPermission,
+ loading: loadingNewPermission,
+ error: errorNewPermission
+ }
+ ] = useMutation(CREATE_PERMISSION, {
+ errorPolicy: 'all'
+ })
+
+ const saveAndGoToNextSection = async () => {
+ try {
+ const newProject = await createProject()
+ setProjectCreated(newProject)
+ goToNextSection()
+ } catch (err) {
+ console.log(err)
+ }
+ }
+
+ const createProject = async () => {
+ const newProjectVariables = {
+ name: selectedRepo.name,
+ github_url: selectedRepo.githubUrl,
+ is_public: !selectedRepo.private
+ }
+ const newProject = await addProject({ variables: newProjectVariables })
+ if (newProject.errors) {
+ throw new Error('An error ocurred while creating the project')
+ }
+ const newPermission = await createPermission({
+ variables: {
+ contributor_id: sessionUser().id,
+ project_id: newProject.data.createProject.id,
+ type: 'owner'
+ }
+ })
+ if (loadingAddProject) return (Loading
)
+ if (newProject.errors) {
+ console.log(`${Object.keys(newProject.errors[0].extensions.exception.fields)[0]} already exists`)
+ } else {
+ const newPermission = await createPermission({
+ variables: {
+ contributor_id: sessionUser().id,
+ project_id: newProject.data.createProject.id,
+ type: 'owner'
+ }
+ })
+ }
+ return newProject
+ }
+ return (
+
+ )
+}
+
+export default CreateProjectOnboarding
\ No newline at end of file
diff --git a/srcv2/components/Footer.js b/srcv2/components/Footer.js
new file mode 100644
index 00000000..fdbfb9eb
--- /dev/null
+++ b/srcv2/components/Footer.js
@@ -0,0 +1,36 @@
+import React from 'react'
+
+import Section from './Section'
+
+import { FOOTER_LINKS, GITHUB_LOGO_URL } from '../constants'
+
+const Footer = () => {
+ const renderSocialMedia = () => {
+ return FOOTER_LINKS.map(social => {
+ return (
+
+
+
+ )
+ })
+
+ }
+ return (
+
+
+
+
+
+ {renderSocialMedia()}
+
+
+
+
+ )
+}
+
+export default Footer
\ No newline at end of file
diff --git a/srcv2/components/GitHubButton.js b/srcv2/components/GitHubButton.js
new file mode 100644
index 00000000..4b39d3bf
--- /dev/null
+++ b/srcv2/components/GitHubButton.js
@@ -0,0 +1,35 @@
+import React from 'react'
+
+import { API_ROOT, GITHUB_LOGO_URL } from '../constants'
+
+const GitHubButton = (props) => {
+
+ const {
+ text
+ } = props
+
+ const handleGithubLogin = () => {
+ window.open(`${API_ROOT}/login`, '_self')
+ }
+
+ return (
+
+ )
+}
+
+export default GitHubButton
\ No newline at end of file
diff --git a/srcv2/components/OnboardingNextSection.js b/srcv2/components/OnboardingNextSection.js
new file mode 100644
index 00000000..88e6de66
--- /dev/null
+++ b/srcv2/components/OnboardingNextSection.js
@@ -0,0 +1,28 @@
+import React from 'react'
+
+import Section from './Section'
+
+const OnboardingNextSection = (props) => {
+ const {
+ goToNextSection
+ } = props
+
+ return (
+
+
+
history.push('/dashboard')}>
+ Skip
+
+
+
+
+ )
+}
+
+export default OnboardingNextSection
\ No newline at end of file
diff --git a/srcv2/components/OpenInGithubButton.js b/srcv2/components/OpenInGithubButton.js
new file mode 100644
index 00000000..c6b089b2
--- /dev/null
+++ b/srcv2/components/OpenInGithubButton.js
@@ -0,0 +1,33 @@
+import React from 'react'
+
+import { GITHUB_ALT_LOGO_URL } from '../constants'
+
+const OpenInGithubButton = (props) => {
+ const {
+ url
+ } = props
+
+ return (
+
+ )
+}
+
+export default OpenInGithubButton
\ No newline at end of file
diff --git a/srcv2/components/Overlay.js b/srcv2/components/Overlay.js
new file mode 100644
index 00000000..f6a2cbe1
--- /dev/null
+++ b/srcv2/components/Overlay.js
@@ -0,0 +1,57 @@
+import React from 'react'
+import { Modal, Icon } from '@material-ui/core'
+
+import Section from './Section'
+
+const Overlay = ({
+ open,
+ setOpen,
+ buttonText,
+ buttonAction,
+ goBackAction,
+ height = '',
+ fullScreen,
+ children,
+ containerClassName,
+ position = 'bottom'
+}) => {
+
+ return (
+
+
{ setOpen(false) }}
+ className={'modal fixed inset-0 flex bg-black bg-opacity-50 items-center justify-center transition-opacity duration-300 z-40'}
+ >
+
+
+
+ { goBackAction() }}
+ />
+ { setOpen(false) }}
+ />
+
+
+ { children }
+
+
+
+
+
+
+ )
+}
+
+export default Overlay
\ No newline at end of file
diff --git a/srcv2/components/PlanFundingOnboarding.js b/srcv2/components/PlanFundingOnboarding.js
new file mode 100644
index 00000000..ef3d16ed
--- /dev/null
+++ b/srcv2/components/PlanFundingOnboarding.js
@@ -0,0 +1,79 @@
+import React, { useEffect, useState } from 'react'
+import { useMutation } from '@apollo/client'
+import { useHistory } from 'react-router-dom'
+
+import CreateProjectFunding from './CreateProjectFunding'
+import OnboardingNextSection from './OnboardingNextSection'
+import Section from './Section'
+
+import { CURRENCIES } from '../constants'
+
+import { UPDATE_PROJECT } from '../operations/mutations/ProjectMutations'
+
+const FUNDING_PLAN_TIMEFRAME_AMOUNTS = ['Monthly amount', 'Total amount', 'Quarterly']
+
+const PlanFundingOnboarding = (props) => {
+
+ const {
+ project
+ } = props
+
+ const history = useHistory()
+
+ const [budgetRange, setBudgetRange] = useState(null)
+ const [currency, setCurrency] = useState(CURRENCIES[0])
+ const [timeframeAmount, setTimeFrameAmount] = useState(FUNDING_PLAN_TIMEFRAME_AMOUNTS[0])
+
+ const [
+ addProjectFunding,
+ {
+ data,
+ loading,
+ error
+ }
+ ] = useMutation(UPDATE_PROJECT, {
+ errorPolicy: 'all'
+ })
+
+ const saveAndGoToNextSection = async () => {
+ try {
+ const fundProjectVariables = {
+ project_id: project.data.createProject.id,
+ expected_budget: budgetRange,
+ expected_budget_timeframe: timeframeAmount,
+ expected_budget_currency: currency.name
+ }
+ await addProjectFunding({
+ variables: fundProjectVariables
+ })
+ history.push('/dashboard')
+ } catch (err) {
+ console.log('err')
+ console.log(err)
+ }
+ return true
+ }
+
+ return (
+
+ )
+}
+
+export default PlanFundingOnboarding
\ No newline at end of file
diff --git a/srcv2/components/PrivateRoute.js b/srcv2/components/PrivateRoute.js
new file mode 100644
index 00000000..21f048a0
--- /dev/null
+++ b/srcv2/components/PrivateRoute.js
@@ -0,0 +1,38 @@
+import React from 'react'
+import { Route, Redirect } from 'react-router-dom'
+import { useReactiveVar, useQuery } from '@apollo/client'
+
+import { authUser } from '../reactivities/variables'
+
+import { CHECK_SESSION } from '../operations/queries/ContributorQueries'
+
+const PrivateRoute = (props) => {
+
+ const { error, loading, data } = useQuery(CHECK_SESSION)
+
+ if (loading) {
+ return (
+ <>
+ >
+ )
+ }
+
+ if (error) return `Error! ${error.message}`
+
+ const isLoggedIn = data.checkSession ? true : false
+
+ const { component: Component, ...rest } = props
+
+ return (
+ (
+ isLoggedIn
+ ?
+ :
+ )}
+ >
+
+ )
+}
+export default PrivateRoute
\ No newline at end of file
diff --git a/srcv2/components/ProjectAdministrationOnboarding.js b/srcv2/components/ProjectAdministrationOnboarding.js
new file mode 100644
index 00000000..7990bd3e
--- /dev/null
+++ b/srcv2/components/ProjectAdministrationOnboarding.js
@@ -0,0 +1,46 @@
+import React from 'react'
+
+import Section from './Section'
+import OnboardingNextSection from './OnboardingNextSection'
+
+import {
+ IPHONE_IMAGE_URL
+} from '../constants'
+
+const ProjectAdministrationOnboarding = (props) => {
+
+ const {
+ goToNextSection
+ } = props
+
+ return (
+
+
+
+
+
+
+ Project Administration
+
+
+ Create projects from any Github repo
+
+
+ View insights for a project
+
+
+
+
![iphone mockup]({IPHONE_IMAGE_URL})
+
+
+
+
+
+ )
+}
+
+export default ProjectAdministrationOnboarding
\ No newline at end of file
diff --git a/srcv2/components/ProjectTile.js b/srcv2/components/ProjectTile.js
new file mode 100644
index 00000000..660f8d1e
--- /dev/null
+++ b/srcv2/components/ProjectTile.js
@@ -0,0 +1,29 @@
+import React from 'react'
+
+const ProjectTile = (props) => {
+
+ const {
+ project
+ } = props
+
+ return (
+
+
+
+
+ {project.name.charAt(0).toUpperCase()}
+
+
+
+
+
{project.expected_budget_currency}
+
+
+
+
+ )
+}
+
+export default ProjectTile
\ No newline at end of file
diff --git a/srcv2/components/ProjectsList.js b/srcv2/components/ProjectsList.js
new file mode 100644
index 00000000..4a413b59
--- /dev/null
+++ b/srcv2/components/ProjectsList.js
@@ -0,0 +1,90 @@
+import React from 'react'
+import {
+ Icon
+} from '@material-ui/core'
+import { useQuery } from '@apollo/client'
+import { useHistory } from 'react-router-dom';
+
+import ProjectTile from './ProjectTile'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { GET_CONTRIBUTOR_PROJECTS } from '../operations/queries/ContributorQueries'
+
+const ProjectsList = () => {
+
+ const history = useHistory()
+
+ const {
+ data: dataContributorProjects,
+ loading: loadingContributorProjects,
+ error: errorContributorProjects
+ } = useQuery(GET_CONTRIBUTOR_PROJECTS, {
+ variables: {
+ id: Number(sessionUser().id)
+ }
+ })
+
+ const renderProjects = () => {
+ return dataContributorProjects.getContributorById.projects.map(project => {
+ return (
+
+
+
+ )
+ })
+ }
+
+ const projectsToShow = !loadingContributorProjects && !!dataContributorProjects.getContributorById.projects.length
+
+ return (
+
+
+
+ {loadingContributorProjects &&
+ `Loading...`
+ }
+ {!projectsToShow &&
+
+ }
+
+ {projectsToShow &&
+
+ {renderProjects()}
+
+ }
+
+
+ )
+}
+
+export default ProjectsList
\ No newline at end of file
diff --git a/srcv2/components/PublicRoute.js b/srcv2/components/PublicRoute.js
new file mode 100644
index 00000000..cf7e75db
--- /dev/null
+++ b/srcv2/components/PublicRoute.js
@@ -0,0 +1,24 @@
+import React from 'react'
+import { Route, Redirect } from 'react-router-dom'
+import { useReactiveVar } from '@apollo/client'
+
+import { authUser } from '../reactivities/variables'
+
+const PublicRoute = (props) => {
+
+ const isLoggedIn = useReactiveVar(authUser)
+
+ const { component: Component, restricted, ...rest } = props
+ return (
+ (
+ isLoggedIn && restricted
+ ?
+ :
+ )}
+ >
+
+ )
+}
+export default PublicRoute
diff --git a/srcv2/components/Section.js b/srcv2/components/Section.js
new file mode 100644
index 00000000..b20cfb22
--- /dev/null
+++ b/srcv2/components/Section.js
@@ -0,0 +1,15 @@
+import React from 'react'
+
+const Section = ({
+ backgroundColor,
+ children,
+ className
+}) => {
+ return (
+
+ { children }
+
+ )
+}
+
+export default Section
\ No newline at end of file
diff --git a/srcv2/components/Selector.js b/srcv2/components/Selector.js
new file mode 100644
index 00000000..eac62fc1
--- /dev/null
+++ b/srcv2/components/Selector.js
@@ -0,0 +1,53 @@
+import React from 'react'
+
+const Selector = ({
+ title,
+ renderOptions,
+ openOptions,
+ setOpenOptions,
+ loadingOptions,
+ margin,
+ buttonClassName
+}) => {
+
+ const selectorOptions = renderOptions()
+
+ return (
+
+
+ {openOptions &&
+
+
+ {loadingOptions &&
+ 'Loading'
+ }
+ {selectorOptions.length == 0 && !loadingOptions ? (
+ 'No options available'
+ ) : (
+ selectorOptions
+ )}
+
+
+ }
+
+ )
+}
+
+Selector.defaultProps = {
+ margin: '12'
+}
+
+export default Selector
\ No newline at end of file
diff --git a/srcv2/components/SendBonus.js b/srcv2/components/SendBonus.js
new file mode 100644
index 00000000..349168ec
--- /dev/null
+++ b/srcv2/components/SendBonus.js
@@ -0,0 +1,154 @@
+import React, { useState, useEffect } from 'react'
+import CurrencyTextField from '@unicef/material-ui-currency-textfield'
+import { useMutation } from '@apollo/client'
+
+import SendBonusAmount from './SendBonusAmount'
+import SendBonusConfirmation from './SendBonusConfirmation'
+import SendBonusSuccessful from './SendBonusSuccessful'
+
+import { CONVERT_USD_TO_SATS_AMOUNT, SEND_BONUS } from '../operations/mutations/PaymentMutations'
+
+const SendBonus = (props) => {
+
+ const {
+ project,
+ setOpen,
+ screenIndex,
+ setScreenIndex
+ } = props
+
+ const [selectedContributors, setSelectedContributors] = useState([])
+ const [bonusAmount, setBonusAmount] = useState(0)
+ const [bonusPayments, setBonusPayments] = useState([])
+ const [selectedBonusSplitType, setSelectedBonusSplitType] = useState(0)
+ const [sentBonuses, setSentBonuses] = useState(0)
+
+ const buttonText = ['Continue', 'Send bonuses', 'Finish']
+
+ const handleSubmitPayments = async (bonusPayments) => {
+ const sentBonusInfo = { succeeded: [], failed: [] }
+ const contributorsWithWallets = []
+ await Promise.all(bonusPayments.map(bp => {
+ try {
+ if (!bp.contributor.wallet) {
+ throw new Error('User does not have a wallet setup')
+ }
+ if (bp.contributor.wallet.invoice_macaroon != null || bp.contributor.wallet.onchain_address != null) {
+ contributorsWithWallets.push({ contributor_id: bp.contributor.id, amount_to_pay: Math.round(bp.satsBonusAmount) })
+ }
+ } catch (error) {
+ console.log('An error occurred: ' + error)
+ sentBonusInfo.failed.push(bp)
+ }
+ }))
+ const bonusSent = await sendBonus({
+ variables: {
+ contributors: contributorsWithWallets
+ }
+ })
+ bonusSent.data.sendPayment.map(bs => {
+ try {
+ if (bs.error) {
+ throw new Error('Transaction error')
+ } else {
+ const paymentSucceeded = bonusPayments.filter(item => item.contributor.id === bs.contributorId)
+ sentBonusInfo.succeeded.push(...paymentSucceeded)
+ }
+ } catch (error) {
+ console.log('An error occurred: ' + error)
+ const paymentFailed = bonusPayments.filter(item => item.contributor.id === bs.contributorId)
+ sentBonusInfo.failed.push(...paymentFailed)
+ }
+ })
+ setSentBonuses(sentBonusInfo)
+ }
+
+ useEffect(() => {
+ const newBonusAmounts = []
+ selectedContributors.map(contributor => {
+ const newBonusAmount = {
+ active: 1,
+ amount: bonusAmount ? bonusAmount.replace(',', '') : 0,
+ contributor: contributor
+ }
+ newBonusAmounts.push(newBonusAmount)
+ })
+ setBonusPayments(newBonusAmounts)
+ }, [bonusAmount, selectedContributors])
+
+ const [fetchSatsAmount] = useMutation(CONVERT_USD_TO_SATS_AMOUNT)
+ const [sendBonus, {
+ data: dataSendBonus,
+ loading: loadingSendBonus,
+ error: errorSendBonus
+ }] = useMutation(SEND_BONUS)
+
+ const nextStep = async () => {
+ if (screenIndex == 0) {
+ if (bonusPayments.length) {
+ await Promise.all(await bonusPayments.map(async bp => {
+ const variables = { amount: parseInt(bp.amount, 10) }
+ const { data } = await fetchSatsAmount({ variables })
+ bp.satsBonusAmount = data.convertUSDtoSATS
+ return data.convertUSDtoSATS
+ }))
+ setBonusPayments(bonusPayments)
+ }
+ } else if (screenIndex == 1) {
+ await handleSubmitPayments(bonusPayments)
+ }
+ setScreenIndex(screenIndex + 1)
+ }
+
+ const enableContinue = (
+ selectedContributors.length && bonusAmount != 0 && selectedBonusSplitType == 0
+ ) || (
+ !bonusPayments.length == 0 && selectedBonusSplitType == 1
+ )
+
+ if (screenIndex == 3) {
+ setOpen(false)
+ setScreenIndex(0)
+ }
+
+ return (
+
+ {screenIndex == 0 &&
+
+ }
+ {screenIndex == 1 &&
+
+ }
+ {screenIndex == 2 &&
+
+ }
+
+
+
+
+ )
+}
+
+export default SendBonus
\ No newline at end of file
diff --git a/srcv2/components/SendBonusAmount.js b/srcv2/components/SendBonusAmount.js
new file mode 100644
index 00000000..72159ada
--- /dev/null
+++ b/srcv2/components/SendBonusAmount.js
@@ -0,0 +1,76 @@
+import React, { useState } from 'react'
+
+import SendBonusEqualy from './SendBonusEqualy'
+import SendBonusCustomize from './SendBonusCustomize'
+
+const BONUS_SPLIT_TYPES = [
+ {
+ type: 'equaly',
+ name: 'Split Equaly'
+ },
+ {
+ type: 'customize',
+ name: 'Customize'
+ }
+]
+
+const SendBonusAmount = (props) => {
+
+ const {
+ project,
+ selectedContributors,
+ setSelectedContributors,
+ bonusAmount,
+ setBonusAmount,
+ bonusPayments,
+ setBonusPayments,
+ selectedBonusSplitType,
+ setSelectedBonusSplitType
+ } = props
+
+ return (
+
+
+ Enter info below to send a bonus
+
+
+
+
+
+ {selectedBonusSplitType == 0 ? (
+
+ ) : (
+
+ )}
+
+ )
+}
+
+export default SendBonusAmount
\ No newline at end of file
diff --git a/srcv2/components/SendBonusConfirmation.js b/srcv2/components/SendBonusConfirmation.js
new file mode 100644
index 00000000..7e23aaec
--- /dev/null
+++ b/srcv2/components/SendBonusConfirmation.js
@@ -0,0 +1,66 @@
+import React from 'react'
+import {
+ Icon
+} from '@material-ui/core'
+
+const SendBonusConfirmation = (props) => {
+
+ const {
+ bonusPayments,
+ selectedBonusSplitType
+ } = props
+
+ const renderSenders = () => {
+ return bonusPayments.map(bp => {
+ return (
+
+
+
+ {bp.contributor.name}
+
+
+
+
+ {`${Intl.NumberFormat().format(Math.trunc(bp.satsBonusAmount))} SATS`}
+
+
+ {`${Intl.NumberFormat('de-DE', { style: 'currency', currency: 'USD' }).format(Math.trunc(bp.amount))}`}
+
+
+
+ )
+ })
+ }
+
+ return (
+
+
+ Confirmation page
+
+
+ Please review the amount below and confirm before sending
+
+
+
+
+ {selectedBonusSplitType == 0 &&
+ <>
+
+ {`${Intl.NumberFormat().format(Math.trunc(bonusPayments[0].satsBonusAmount))} SATS`}
+
+
+ {`~ ${Intl.NumberFormat('de-DE', { style: 'currency', currency: 'USD' }).format(Math.trunc(bonusPayments[0].amount))}`}
+
+ >
+ }
+
+ Sending to
+
+
+ {renderSenders()}
+
+
+ )
+}
+
+export default SendBonusConfirmation
\ No newline at end of file
diff --git a/srcv2/components/SendBonusCustomize.js b/srcv2/components/SendBonusCustomize.js
new file mode 100644
index 00000000..45672064
--- /dev/null
+++ b/srcv2/components/SendBonusCustomize.js
@@ -0,0 +1,140 @@
+import React, { useEffect, useState } from 'react'
+import CurrencyTextField from '@unicef/material-ui-currency-textfield'
+import { Icon } from '@material-ui/core'
+
+import { selectCurrencyInformation } from '../scripts/selectors'
+
+const SendBonusCustomize = (props) => {
+
+ const {
+ project,
+ setBonusPayments,
+ } = props
+
+ const [selectedContributorsIdx, setSelectedContributorsIdx] = useState([])
+ const [localBonusPayments, setLocalBonusPayments] = useState([])
+
+ useEffect(() => {
+ setBonusPayments(localBonusPayments)
+ }, [localBonusPayments])
+
+ useEffect(() => {
+ const bonuses = localBonusPayments
+ localBonusPayments.map((b, idx) => {
+ if (selectedContributorsIdx.includes(b.idx)) {
+ const activeBonus = b
+ activeBonus.active = 1
+ bonuses.splice(idx, 1)
+ setLocalBonusPayments([...bonuses, activeBonus])
+ } else {
+ const inactiveBonus = b
+ inactiveBonus.active = 0
+ bonuses.splice(idx, 1)
+ setLocalBonusPayments([...bonuses, inactiveBonus])
+ }
+ })
+ }, [selectedContributorsIdx])
+
+ const currencyInformation = project.expected_budget_currency
+ ? selectCurrencyInformation({
+ currency: project.expected_budget_currency
+ })
+ : null
+
+ const selectContributor = (contributor, idx) => {
+ if (selectedContributorsIdx.includes(idx)) {
+ setSelectedContributorsIdx(
+ selectedContributorsIdx.filter(c => c != idx)
+ )
+ } else {
+ setSelectedContributorsIdx([...selectedContributorsIdx, idx])
+ }
+ }
+
+ const handleBonusAmountChange = (contributor, amount, idx) => {
+ if (amount.includes('.')) {
+ amount = amount.slice(0, amount.indexOf('.'))
+ }
+ let bonusIdx = -1
+ localBonusPayments.map((b, idx) => {
+ if (b.contributor.id == contributor.id) {
+ bonusIdx = idx
+ }
+ })
+ const bonuses = localBonusPayments
+ if (bonusIdx != -1) {
+ bonuses.splice(bonusIdx, 1)
+ }
+ const newBonusAmount = {
+ active: 1,
+ amount: amount,
+ idx: idx,
+ contributor: contributor
+ }
+ setLocalBonusPayments([...bonuses, newBonusAmount])
+ }
+
+ const renderContributors = (contributors) => {
+ return contributors.map((c, idx) => {
+ const isSelected = selectedContributorsIdx.includes(idx)
+ return (
+
+
+
+
+
+
+
+ handleBonusAmountChange(c, event.target.value, idx)}
+ disabled={!isSelected}
+ />
+
+
+ )
+ })
+ }
+
+ const allContributorsSelected = selectedContributorsIdx.length == project.contributors.length
+ const indicesArray = Array.from({ length: project.contributors.length }, (value, index) => index);
+
+ return (
+
+
+
+ Active contributors for this project
+
+
+
+ {renderContributors(project.contributors)}
+
+
+
+ )
+}
+
+export default SendBonusCustomize
\ No newline at end of file
diff --git a/srcv2/components/SendBonusEqualy.js b/srcv2/components/SendBonusEqualy.js
new file mode 100644
index 00000000..d4beda81
--- /dev/null
+++ b/srcv2/components/SendBonusEqualy.js
@@ -0,0 +1,108 @@
+import React, { useState, useEffect } from 'react'
+import CurrencyTextField from '@unicef/material-ui-currency-textfield'
+import { Icon } from '@material-ui/core'
+
+import { selectCurrencyInformation } from '../scripts/selectors'
+
+const SendBonusEqualy = (props) => {
+
+ const {
+ setSelectedContributors,
+ project,
+ setBonusAmount
+ } = props
+
+ const [selectedContributorsIdx, setSelectedContributorsIdx] = useState([])
+
+ useEffect(() => {
+ const contributors = project.contributors.filter((c, idx) => selectedContributorsIdx.includes(idx))
+ setSelectedContributors(contributors)
+ }, [selectedContributorsIdx])
+
+ const currencyInformation =
+ selectCurrencyInformation({
+ currency: 'USD'
+ })
+
+ const selectContributor = (idx) => {
+ if (selectedContributorsIdx.includes(idx)) {
+ setSelectedContributorsIdx(
+ selectedContributorsIdx.filter(c => c != idx)
+ )
+ return
+ }
+ setSelectedContributorsIdx([...selectedContributorsIdx, idx])
+ }
+
+ const handleBonusAmountChange = (amount) => {
+ if (amount.includes('.')) {
+ setBonusAmount(amount.slice(0, amount.indexOf('.')))
+ return
+ }
+ setBonusAmount(amount)
+ }
+
+ const allContributorsSelected = selectedContributorsIdx.length == project.contributors.length
+
+ const renderContributors = (contributors) => {
+ return contributors.map((c, idx) => {
+ const isSelected = selectedContributorsIdx.includes(idx)
+ return (
+
+
+
+
+
+
+
+ )
+ })
+ }
+
+ const indicesArray = Array.from({ length: project.contributors.length }, (value, index) => index);
+
+ return (
+
+
+ handleBonusAmountChange(event.target.value)}
+ />
+
+
+
+ Active contributors for this project
+
+
+
+ {renderContributors(project.contributors)}
+
+
+
+ )
+}
+
+export default SendBonusEqualy
\ No newline at end of file
diff --git a/srcv2/components/SendBonusSuccessful.js b/srcv2/components/SendBonusSuccessful.js
new file mode 100644
index 00000000..c3ef0770
--- /dev/null
+++ b/srcv2/components/SendBonusSuccessful.js
@@ -0,0 +1,96 @@
+import React from 'react'
+import {
+ Icon,
+} from '@material-ui/core'
+
+const SendBonusSuccessful = (props) => {
+
+ const {
+ sentBonuses
+ } = props
+
+ const renderSuccessfulTransactions = (payments) => {
+ return payments.map(bp => {
+ return (
+
+
+
+ {bp.contributor.name}
+
+
+
+
+ {`${Intl.NumberFormat().format(Math.trunc(bp.satsBonusAmount))} SATS`}
+
+
+ {`${Intl.NumberFormat('de-DE', { style: 'currency', currency: 'USD' }).format(Math.trunc(bp.amount))}`}
+
+
+
+ )
+ })
+ }
+
+ const renderFailedTransactions = (payments) => {
+ return payments.map(bp => {
+ return (
+
+
+
+ {bp.contributor.name}
+
+
+
+
+ {`${Intl.NumberFormat().format(Math.trunc(bp.satsBonusAmount))} SATS`}
+
+
+ {`${Intl.NumberFormat('de-DE', { style: 'currency', currency: 'USD' }).format(Math.trunc(bp.amount))}`}
+
+
+
+ )
+ })
+ }
+
+ return (
+
+
+
history.push(`/add-payment/${projectId}`)}
+ >
+
+
+
+ Bonuses have been sent!
+
+
+
+
+ {(sentBonuses.succeeded && sentBonuses.succeeded.length != 0) &&
+
+ {renderSuccessfulTransactions(sentBonuses.succeeded)}
+
+ }
+ {(sentBonuses.failed && sentBonuses.failed.length != 0) &&
+ <>
+
+
+
+ Some transactions failed:
+
+
+
+ {renderFailedTransactions(sentBonuses.failed)}
+
+
+ >
+ }
+
+
+ )
+}
+
+export default SendBonusSuccessful
\ No newline at end of file
diff --git a/srcv2/components/Step.js b/srcv2/components/Step.js
new file mode 100644
index 00000000..1dae8689
--- /dev/null
+++ b/srcv2/components/Step.js
@@ -0,0 +1,18 @@
+import React from 'react'
+
+const Step = ({
+ title,
+ number
+}) => {
+
+ return (
+
+ )
+}
+
+export default Step
\ No newline at end of file
diff --git a/srcv2/components/WalletOption.js b/srcv2/components/WalletOption.js
new file mode 100644
index 00000000..3d289353
--- /dev/null
+++ b/srcv2/components/WalletOption.js
@@ -0,0 +1,51 @@
+import React from 'react'
+import { useHistory } from 'react-router-dom';
+import Section from './Section'
+import {
+ Icon
+} from '@material-ui/core'
+
+const WalletOption = ({
+ icon,
+ title,
+ subtitle,
+ route,
+ count,
+ disabled,
+ completed
+}) => {
+
+ const history = useHistory()
+
+ const handleButton = () => {
+ if (!disabled) {
+ history.push(route)
+ }
+ }
+
+ return (
+
+
+
+ )
+}
+
+export default WalletOption
\ No newline at end of file
diff --git a/srcv2/components/WalletSimpleSetupOnboarding.js b/srcv2/components/WalletSimpleSetupOnboarding.js
new file mode 100644
index 00000000..3824878b
--- /dev/null
+++ b/srcv2/components/WalletSimpleSetupOnboarding.js
@@ -0,0 +1,182 @@
+import React, { useState } from 'react'
+import {
+ Icon
+} from '@material-ui/core'
+
+import Step from './Step'
+
+import {
+ MUUN_ICON_URL,
+ WALLET_OF_SATOSHI_ICON_URL,
+ ZEUS_ICON_URL
+} from '../constants'
+
+const WalletSimpleSetupOnboarding = ({
+ setOnboardingScreenIndex,
+ onboardingScreenIndex,
+ setOpen
+}) => {
+
+ const goToNextScreen = () => {
+ if (onboardingScreenIndex >= onboardingScreenRenders.length - 1) {
+ setOpen(false)
+ return
+ }
+ setOnboardingScreenIndex(onboardingScreenIndex + 1)
+ }
+
+ const renderFirst = () => {
+ const SETUP_STEPS = [
+ {
+ text: 'Download a wallet'
+ },
+ {
+ text: 'Create a BTC address'
+ },
+ {
+ text: 'Complete your security backup'
+ },
+ {
+ text: 'Receive payments'
+ }
+ ]
+ const renderSetupSteps = () => {
+ return SETUP_STEPS.map((step, idx) => {
+ return (
+
+ )
+ })
+ }
+ return (
+
+
Simple Option
+
+
+ To receive payments in Trinary you need a BTC address to link the account
+
+
+ {renderSetupSteps()}
+
+ )
+ }
+
+ const renderSecond = () => {
+ const renderWallets = () => {
+ const WALLETS = [
+ {
+ name: 'Wallet of Satoshi',
+ walletWeb: 'https://www.walletofsatoshi.com/',
+ icon: WALLET_OF_SATOSHI_ICON_URL
+ },
+ {
+ name: 'Muun',
+ walletWeb: 'https://muun.com/',
+ icon: MUUN_ICON_URL
+ },
+ {
+ name: 'Zeus',
+ walletWeb: 'https://zeusln.app/about',
+ icon: ZEUS_ICON_URL
+ },
+ ]
+ return WALLETS.map(wallet => {
+ return (
+
+
+
+ )
+ })
+ }
+ return (
+
+
+
Download a Wallet of Preference
+
+
+ {renderWallets()}
+
+
+
+ )
+ }
+
+ const renderThird = () => {
+ return (
+
+
+
Create Bitcoin Address
+
+
+ Follow the step-by-step guide of your downloaded wallet and create a BTC address
+
+
+
+ )
+ }
+
+ const renderFourth = () => {
+ return (
+
+
+
Back Up your Wallet
+
+
+ Remember to create a backup to never lose access to your wallet
+
+
+
+
+
+
+
+ )
+ }
+
+ const renderFifth = () => {
+ return (
+
+
+
Start Receiving Payments
+
+ )
+ }
+
+ const renderOnboardingScreens = () => {
+ return onboardingScreenRenders[onboardingScreenIndex]
+ }
+
+ const onboardingScreenRenders = [renderFirst(), renderSecond(), renderThird(), renderFourth(), renderFifth()]
+
+ return (
+
+ {renderOnboardingScreens()}
+
+
+
+
+ )
+}
+
+export default WalletSimpleSetupOnboarding
\ No newline at end of file
diff --git a/srcv2/constants/index.js b/srcv2/constants/index.js
new file mode 100644
index 00000000..e49653b8
--- /dev/null
+++ b/srcv2/constants/index.js
@@ -0,0 +1,117 @@
+export const API_ROOT = process.env.REACT_APP_API_URL
+export const TEMP_AUTHORIZED_SUPERUSER_ID = process.env.TEMP_AUTHORIZED_SUPERUSER_ID
+export const IS_PRODUCTION = process.env.NODE_ENV == 'production' ? true : false
+export const GITHUB_LOGO_URL = 'https://project-trinary.s3.us-east-1.amazonaws.com/images/github-icon.png'
+export const GITHUB_ALT_LOGO_URL = 'https://setlife-solutions.s3.us-east-1.amazonaws.com/images/github-alt-logo.png'
+export const HERO_IMAGE_URL = 'https://project-trinary.s3.us-east-1.amazonaws.com/images/landing-banner.png'
+export const IPHONE_IMAGE_URL = 'https://project-trinary.s3.us-east-1.amazonaws.com/images/iphone-landing-mockup.png'
+export const BUDGETING_IMAGE_URL = 'https://project-trinary.s3.us-east-1.amazonaws.com/images/budget-landing-mockup.png'
+export const WALLET_OF_SATOSHI_ICON_URL = 'https://project-trinary.s3.us-east-1.amazonaws.com/images/wallet-of-satoshi.png'
+export const MUUN_ICON_URL = 'https://play-lh.googleusercontent.com/8fbesX3FwtlJXy_W72BOHGqbMM220XX3q6O7K3xE04XyLR9Pz9wwDdZOyjklYG1Lm8o'
+export const ZEUS_ICON_URL = 'https://store.zeusln.app/wp-content/uploads/2022/03/Word-Logo-300x153.png'
+export const CURRENCIES = [
+ {
+ name: 'USD',
+ symbol: '$',
+ decimal: '.',
+ thousand: ',',
+ precision: 2,
+ },
+ {
+ name: 'MXN',
+ symbol: '$',
+ decimal: '.',
+ thousand: ',',
+ precision: 2,
+ },
+ {
+ name: 'EUR',
+ symbol: '€',
+ decimal: ',',
+ thousand: '.',
+ precision: 2,
+ },
+ {
+ name: 'BTC',
+ symbol: '₿',
+ decimal: '.',
+ thousand: ',',
+ precision: 2,
+ },
+ {
+ name: 'SATS',
+ symbol: 's ',
+ decimal: '.',
+ thousand: ',',
+ precision: 0,
+ },
+ {
+ name: '',
+ symbol: '',
+ decimal: '.',
+ thousand: ',',
+ precision: 2,
+ }
+]
+export const FOOTER_LINKS = [
+ {
+ type: 'GitHub',
+ logo: GITHUB_LOGO_URL,
+ url: 'https://github.com/setlife-network'
+ },
+ {
+ type: 'YouTube',
+ logo: 'https://project-trinary.s3.us-east-1.amazonaws.com/images/youtube-icon.png',
+ url: ''
+ },
+ {
+ type: 'Twitter',
+ logo: 'https://project-trinary.s3.us-east-1.amazonaws.com/images/twitter-icon.png',
+ url: ''
+ },
+ {
+ type: 'LinkedIn',
+ logo: 'https://project-trinary.s3.us-east-1.amazonaws.com/images/linkedin-vector.png',
+ url: 'https://www.linkedin.com/company/setlife-network/'
+ }
+]
+export const WALLET_OPTIONS = [
+ {
+ icon: 'key',
+ title: 'Simple',
+ subtitle: 'Provide a BTC address',
+ route: 'setup/simple',
+ disabled: false,
+ attribute: 'onchain_address'
+ },
+ {
+ icon: 'wallet',
+ title: 'Advanced',
+ subtitle: 'Download External Wallet',
+ route: 'setup/advanced',
+ disabled: false,
+ attribute: 'lnd_host'
+ },
+ {
+ icon: 'network-wired',
+ title: 'Federated',
+ subtitle: 'Connect your Node',
+ route: 'setup/federated',
+ disabled: true
+ }
+]
+export const NODE_OPTIONS = [
+ {
+ name: 'LND',
+ active: true
+ },
+ {
+ name: 'CLD',
+ active: false
+ },
+ {
+ name: 'Eclair',
+ active: false
+ },
+]
+export const FUNDING_PLAN_TIMEFRAME_AMOUNTS = ['Monthly amount', 'Total amount', 'Quarterly']
\ No newline at end of file
diff --git a/srcv2/index.js b/srcv2/index.js
new file mode 100644
index 00000000..52e40091
--- /dev/null
+++ b/srcv2/index.js
@@ -0,0 +1,31 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { BrowserRouter } from 'react-router-dom'
+import { ApolloClient, ApolloProvider, InMemoryCache, createHttpLink } from '@apollo/client'
+
+import './styles/index.scss'
+import App from './App'
+import { API_ROOT } from './constants'
+
+const cache = new InMemoryCache()
+const uri = `${API_ROOT}/graph`
+const link = createHttpLink({
+ uri,
+ credentials: 'include'
+})
+const client = new ApolloClient({
+ link,
+ cache,
+ connectToDevTools: true
+})
+
+ReactDOM.render(
+
+
+
+
+
+
+ ,
+ document.getElementById('root')
+)
diff --git a/srcv2/operations/mutations/AllocationMutations.js b/srcv2/operations/mutations/AllocationMutations.js
new file mode 100644
index 00000000..47fc8d99
--- /dev/null
+++ b/srcv2/operations/mutations/AllocationMutations.js
@@ -0,0 +1,27 @@
+import { gql } from '@apollo/client';
+
+export const UPDATE_ALLOCATION = gql`
+ mutation UpdateAllocation(
+ $id: Int!,
+ $status: String,
+ $amount: Int,
+ $start_date: String,
+ $end_date: String,
+ $rate_id: Int,
+ $payment_id: Int
+ ) {
+ updateAllocationById(
+ id: $id
+ updateFields: {
+ amount: $amount
+ start_date: $start_date
+ end_date: $end_date
+ rate_id: $rate_id
+ payment_id: $payment_id
+ status: $status
+ }
+ ){
+ id
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/mutations/ContributorMutations.js b/srcv2/operations/mutations/ContributorMutations.js
new file mode 100644
index 00000000..b47c9d13
--- /dev/null
+++ b/srcv2/operations/mutations/ContributorMutations.js
@@ -0,0 +1,53 @@
+import { gql } from '@apollo/client'
+
+export const CREATE_RATE = gql`
+ mutation createRate(
+ $total_expected_hours: Int,
+ $hourly_rate: String!,
+ $minimum_expected_hours: Int,
+ $maximum_expected_hours: Int,
+ $minimum_hourly_rate: String,
+ $maximum_hourly_rate: String,
+ $type: String!,
+ $currency: String,
+ $contributor_id: Int!){
+ createRate(createFields: {
+ active: true
+ total_expected_hours: $total_expected_hours
+ hourly_rate: $hourly_rate
+ minimum_expected_hours: $minimum_expected_hours
+ maximum_expected_hours: $maximum_expected_hours
+ minimum_hourly_rate: $minimum_hourly_rate
+ maximum_hourly_rate: $maximum_hourly_rate
+ type: $type
+ currency: $currency
+ contributor_id: $contributor_id
+ }){
+ id
+ }
+ }
+`
+
+export const CREATE_ALLOCATION = gql`
+ mutation CreateAllocation(
+ $amount: Int!,
+ $active: Boolean!,
+ $start_date: String!,
+ $contributor_id: Int!,
+ $project_id: Int!,
+ $rate_id: Int!,
+ $status: String!
+ ) {
+ createAllocation(createFields: {
+ amount: $amount
+ active: $active,
+ start_date: $start_date,
+ contributor_id: $contributor_id,
+ project_id: $project_id,
+ rate_id: $rate_id,
+ status: $status
+ }) {
+ id
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/mutations/PaymentMutations.js b/srcv2/operations/mutations/PaymentMutations.js
new file mode 100644
index 00000000..e31e7a42
--- /dev/null
+++ b/srcv2/operations/mutations/PaymentMutations.js
@@ -0,0 +1,63 @@
+import { gql } from '@apollo/client'
+
+export const CREATE_PAYMENT = gql`
+ mutation CreatePayment($project_id: Int!, $amount: Int!, $date_incurred: String!, $date_paid: String, $currency: String, $contributor_id: Int) {
+ createPayment(createFields: {
+ amount: $amount
+ project_id: $project_id
+ date_incurred: $date_incurred
+ date_paid: $date_paid
+ currency: $currency
+ contributor_id: $contributor_id
+ }){
+ id
+ amount
+ }
+ }
+`
+
+export const UPDATE_PAYMENT = gql`
+ mutation UpdatePayment($id: Int!, $amount: Int!, $date_incurred: String!, $date_paid: String) {
+ updatePaymentById(
+ id: $id,
+ updateFields: {
+ amount: $amount
+ date_incurred: $date_incurred
+ date_paid: $date_paid
+ }
+ ) {
+ id
+ amount
+ date_incurred
+ date_paid
+ }
+ }
+`
+
+export const CREATE_BITCOIN_INVOICE = gql`
+ mutation GenerateBitcoinInvoice($paymentId: Int!){
+ generateBitcoinInvoiceFromPayment(paymentId: $paymentId) {
+ external_uuid
+ bitcoinCheckoutUrl
+ }
+ }
+`
+
+export const CONVERT_USD_TO_SATS_AMOUNT = gql`
+ mutation ConvertUSDtoSATS($amount: Int!) {
+ convertUSDtoSATS(amount: $amount)
+ }
+`
+
+export const SEND_BONUS = gql`
+ mutation SendPayment($contributors: [ContributorBonus]){
+ sendPayment(contributors: $contributors) {
+ contributorId,
+ amount,
+ transactionHash,
+ paymentRequest
+ status,
+ error
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/mutations/PermissionMutations.js b/srcv2/operations/mutations/PermissionMutations.js
new file mode 100644
index 00000000..a630f0f7
--- /dev/null
+++ b/srcv2/operations/mutations/PermissionMutations.js
@@ -0,0 +1,13 @@
+import { gql } from '@apollo/client';
+
+export const CREATE_PERMISSION = gql`
+ mutation CreatePermission($contributor_id: Int!, $project_id: Int!, $type: String!) {
+ createPermission(createFields: {
+ contributor_id: $contributor_id
+ project_id: $project_id
+ type: $type
+ }) {
+ id
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/mutations/ProjectMutations.js b/srcv2/operations/mutations/ProjectMutations.js
new file mode 100644
index 00000000..fb5ba697
--- /dev/null
+++ b/srcv2/operations/mutations/ProjectMutations.js
@@ -0,0 +1,59 @@
+import { gql } from '@apollo/client';
+
+export const ADD_PROJECT = gql`
+ mutation createProject(
+ $name: String!,
+ $github_url: String!,
+ $is_public: Boolean!
+ ){
+ createProject(createFields: {
+ name: $name,
+ github_url: $github_url,
+ is_public: $is_public,
+ is_active: true
+ }){
+ id,
+ name
+ }
+ }
+`
+
+export const UPDATE_PROJECT = gql`
+ mutation updateProjectById(
+ $project_id: Int!,
+ $date: String,
+ $end_date: String,
+ $expected_budget: Int,
+ $expected_budget_currency: String,
+ $name: String,
+ $github_url: String,
+ $expected_budget_timeframe: String,
+ $toggl_url: String
+ $is_active: Boolean
+ ){
+ updateProjectById(
+ id: $project_id,
+ updateFields: {
+ name: $name
+ github_url: $github_url
+ expected_budget_timeframe: $expected_budget_timeframe
+ expected_budget_currency: $expected_budget_currency
+ toggl_url: $toggl_url
+ date: $date
+ end_date: $end_date
+ expected_budget: $expected_budget
+ is_active: $is_active
+ }
+ ){
+ id,
+ name,
+ end_date,
+ expected_budget,
+ expected_budget_timeframe,
+ is_active,
+ github_url,
+ toggl_url
+ is_active
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/mutations/WalletMutations.js b/srcv2/operations/mutations/WalletMutations.js
new file mode 100644
index 00000000..b827db2f
--- /dev/null
+++ b/srcv2/operations/mutations/WalletMutations.js
@@ -0,0 +1,27 @@
+import { gql } from '@apollo/client';
+
+export const UPDATE_WALLET_ADDRESS = gql`
+ mutation updateContributorOnChainAddress(
+ $address: String!
+ ) {
+ updateContributorOnChainAddress(address: $address) {
+ id
+ }
+ }
+`
+
+export const UPDATE_NODE = gql`
+ mutation updateContributorNode(
+ $host: String,
+ $port: Int,
+ $macaroon: String
+ ){
+ updateContributorNode(
+ host: $host
+ port: $port
+ macaroon: $macaroon
+ ){
+ id
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/queries/ContributorQueries.js b/srcv2/operations/queries/ContributorQueries.js
new file mode 100644
index 00000000..2df77de5
--- /dev/null
+++ b/srcv2/operations/queries/ContributorQueries.js
@@ -0,0 +1,121 @@
+import { gql } from '@apollo/client'
+
+export const CHECK_SESSION = gql`
+ query CheckSession{
+ checkSession {
+ id
+ name
+ github_id
+ github_handle
+ github_access_token
+ toggl_id
+ external_data_url
+ totalPaid
+ wallet {
+ onchain_address
+ invoice_macaroon
+ lnd_host
+ lnd_port
+ }
+ }
+ }
+`
+
+export const GET_CONTRIBUTOR_ORGANIZATIONS_FROM_GITHUB = gql`
+ query GithubOrganizations($id: Int) {
+ getGithubOrganizations(contributorId: $id) {
+ id
+ avatar
+ name
+ }
+ }
+`
+
+export const GET_CONTRIBUTOR_REPOS_FROM_GITHUB = gql`
+ query GithubOrganizationRepos($githubPageNumber: Int!, $accountId: Int!) {
+ getGithubRepos(
+ githubPageNumber: $githubPageNumber,
+ accountId: $accountId
+ )
+ {
+ id
+ name
+ githubUrl
+ private
+ }
+ }
+`
+
+export const GET_CONTRIBUTOR_ALLOCATIONS = gql`
+ query ContributorAllocation($id: Int!) {
+ getContributorById(id: $id) {
+ id
+ allocations {
+ id
+ amount
+ active
+ date_paid
+ status
+ proposedBy {
+ id
+ name
+ }
+ project {
+ id
+ name
+ client {
+ id
+ name
+ }
+ }
+ }
+ }
+ }
+`
+
+export const GET_CONTRIBUTOR_PROJECTS = gql`
+ query ContributorProjects($id: Int!) {
+ getContributorById(id: $id) {
+ id
+ wallet {
+ onchain_address
+ invoice_macaroon
+ lnd_host
+ lnd_port
+ }
+ projects {
+ id
+ name
+ is_active
+ expected_budget_currency
+ admin {
+ id
+ name
+ }
+ }
+ }
+ }
+`
+
+export const GET_CONTRIBUTOR_WALLETS = gql`
+ query ContributorWallets($id: Int!) {
+ getContributorById(id: $id) {
+ wallet {
+ onchain_address
+ invoice_macaroon
+ lnd_host
+ lnd_port
+ }
+ }
+ }
+`
+
+export const GET_CONTRIBUTORS = gql`
+ query Contributos {
+ getContributors {
+ id
+ name
+ github_handle
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/queries/PaymentQueries.js b/srcv2/operations/queries/PaymentQueries.js
new file mode 100644
index 00000000..327b4e30
--- /dev/null
+++ b/srcv2/operations/queries/PaymentQueries.js
@@ -0,0 +1,16 @@
+import { gql } from '@apollo/client';
+
+export const GET_PAYMENT_DETAILS = gql`
+ query getPaymentAndClientDetails($paymentId: Int!) {
+ getPaymentById(id: $paymentId) {
+ id
+ amount
+ project_id
+ date_incurred
+ date_paid
+ bitcoinCheckoutUrl
+ isBitcoinInvoiceExpired
+ currency
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/operations/queries/ProjectQueries.js b/srcv2/operations/queries/ProjectQueries.js
new file mode 100644
index 00000000..324df76e
--- /dev/null
+++ b/srcv2/operations/queries/ProjectQueries.js
@@ -0,0 +1,72 @@
+import { gql } from '@apollo/client'
+
+export const GET_PROJECT = gql`
+ query Project($id: Int!){
+ getProjectById(id: $id){
+ id
+ name
+ is_active
+ expected_budget
+ github_url
+ toggl_url
+ date
+ end_date
+ totalPaid
+ expected_budget_timeframe
+ expected_budget_currency
+ totalPaid
+ githubPullRequestsOpened
+ githubPullRequestsClosed
+ githubPullRequestsMerged
+ githubIssuesOpened
+ githubIssuesClosed
+ contributors {
+ id
+ name
+ avatar_url
+ wallet {
+ onchain_address
+ invoice_macaroon
+ lnd_host
+ lnd_port
+ }
+ }
+ client {
+ id
+ name
+ currency
+ }
+ allocations {
+ id
+ active
+ contributor {
+ id
+ github_handle
+ name
+ }
+ }
+ payments {
+ id
+ amount
+ currency
+ contributor {
+ id
+ name
+ github_handle
+ }
+ }
+ }
+ }
+`
+
+export const GET_PROJECT_CONTRIBUTORS = gql`
+ query ProjectContributors($id: Int!){
+ getProjectById(id: $id) {
+ contributors {
+ id
+ name
+ avatar_url
+ }
+ }
+ }
+`
\ No newline at end of file
diff --git a/srcv2/pages/AddPaymentPage.js b/srcv2/pages/AddPaymentPage.js
new file mode 100644
index 00000000..b6b976e6
--- /dev/null
+++ b/srcv2/pages/AddPaymentPage.js
@@ -0,0 +1,157 @@
+import React, { useState } from 'react'
+import { useHistory, useParams } from 'react-router-dom'
+import { useQuery, useMutation } from '@apollo/client'
+import {
+ MuiPickersUtilsProvider,
+ KeyboardDatePicker
+} from '@material-ui/pickers'
+import MomentUtils from '@date-io/moment'
+import moment from 'moment'
+import CurrencyTextField from '@unicef/material-ui-currency-textfield'
+
+import Section from '../components/Section'
+
+import { selectCurrencyInformation } from '../scripts/selectors'
+
+import { GET_PROJECT } from '../operations/queries/ProjectQueries'
+import { CREATE_PAYMENT } from '../operations/mutations/PaymentMutations'
+
+import { sessionUser } from '../reactivities/variables'
+
+const AddPaymentPage = () => {
+
+ const { projectId } = useParams()
+
+ const [paymentAmount, setPaymentAmount] = useState(null)
+ const [paymentIncurred, setPaymentIncurred] = useState(null)
+ const [paymentPaid, setPaymentPaid] = useState(null)
+
+ const history = useHistory()
+
+ const {
+ data: dataProject,
+ loading: loadingProject,
+ error: errorProject,
+ } = useQuery(GET_PROJECT, {
+ variables: {
+ id: Number(projectId)
+ }
+ })
+
+ const [createPayment, {
+ dataNewPayment,
+ loadingNewPayment,
+ errorNewPayment
+ }] = useMutation(CREATE_PAYMENT)
+
+ if (loadingProject) return ('Loading...')
+
+ if (errorProject) return (`${errorProject}`)
+
+ const project = dataProject.getProjectById
+
+ const disabledPayment = !paymentAmount || !paymentIncurred
+
+ const currencyInformation =
+ selectCurrencyInformation({
+ currency: project.expected_budget_currency
+ ? project.expected_budget_currency
+ : 'USD'
+ })
+
+ const handleCreatePayment = async () => {
+ if (disabledPayment) return
+ const variables = {
+ amount: paymentAmount,
+ project_id: Number(projectId),
+ date_incurred: paymentIncurred,
+ date_paid: paymentPaid,
+ currency: project.expected_budget_currency,
+ contributor_id: sessionUser().id
+ }
+ const newPayment = await createPayment({ variables })
+ if (loadingNewPayment) return 'Loading...'
+ if (newPayment.errors) {
+ return `An error ocurred ${Object.keys(newPayment.errors[0].extensions.exception.fields)[0]}`
+ }
+ const {
+ createPayment: payment
+ } = newPayment.data
+ history.push(`/payments/edit/${payment.id}`)
+ }
+
+ const cancelPayment = () => {
+ history.push(`/projects/${project.id}`)
+ }
+
+ return (
+
+
+
+
+ Enter info below to add a payment
+
+
setPaymentAmount(parseInt(value, 10))}
+ />
+
+ setPaymentIncurred(moment(e['_d']).format('YYYY-MM-DD'))}
+ />
+
+
+ setPaymentPaid(moment(e['_d']).format('YYYY-MM-DD'))}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export default AddPaymentPage
\ No newline at end of file
diff --git a/srcv2/pages/CreateProjectPage.js b/srcv2/pages/CreateProjectPage.js
new file mode 100644
index 00000000..cb3ec675
--- /dev/null
+++ b/srcv2/pages/CreateProjectPage.js
@@ -0,0 +1,226 @@
+import React, { useState } from 'react'
+import { useMutation } from '@apollo/client'
+import { useHistory } from 'react-router-dom'
+import {
+ Snackbar
+} from '@material-ui/core'
+import Alert from '@material-ui/lab/Alert'
+
+import CreateProject from '../components/CreateProject'
+import CreateProjectFunding from '../components/CreateProjectFunding'
+import Section from '../components/Section'
+
+import { ADD_PROJECT, UPDATE_PROJECT } from '../operations/mutations/ProjectMutations'
+import { CREATE_PERMISSION } from '../operations/mutations/PermissionMutations'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { CURRENCIES, FUNDING_PLAN_TIMEFRAME_AMOUNTS } from '../constants'
+
+const CreateProjectPage = () => {
+
+ const [currentProjectCreationPhase, setCurrentProjectCreationPhase] = useState(0)
+ const [selectedUser, setSelectedUser] = useState(null)
+ const [selectedRepo, setSelectedRepo] = useState(null)
+ const [budgetRange, setBudgetRange] = useState(null)
+ const [currency, setCurrency] = useState(CURRENCIES[0])
+ const [timeframeAmount, setTimeFrameAmount] = useState(FUNDING_PLAN_TIMEFRAME_AMOUNTS[0])
+ const [newProject, setNewProject] = useState()
+ const [errorMessage, setErrorMessage] = useState('')
+
+ const history = useHistory()
+
+ const [
+ addProject,
+ {
+ data: addProjectData,
+ loading: loadingAddProject,
+ error: errorProjectData
+ }
+ ] = useMutation(ADD_PROJECT, {
+ errorPolicy: 'all'
+ })
+
+ const [
+ addProjectFunding,
+ {
+ data,
+ loading,
+ error
+ }
+ ] = useMutation(UPDATE_PROJECT, {
+ errorPolicy: 'all'
+ })
+
+ const [
+ createPermission,
+ {
+ data: dataNewPermission,
+ loading: loadingNewPermission,
+ error: errorNewPermission
+ }
+ ] = useMutation(CREATE_PERMISSION, {
+ errorPolicy: 'all'
+ })
+
+ const createProject = async () => {
+ const newProjectVariables = {
+ name: selectedRepo.name,
+ github_url: selectedRepo.githubUrl,
+ is_public: !selectedRepo.private
+ }
+ const newProject = await addProject({ variables: newProjectVariables })
+ if (newProject.errors) {
+ if (newProject.errors[0] && newProject.errors[0].message) {
+ throw new Error(newProject.errors[0].message)
+ }
+ throw new Error('An error ocurred while creating the project')
+ }
+ const newPermission = await createPermission({
+ variables: {
+ contributor_id: sessionUser().id,
+ project_id: newProject.data.createProject.id,
+ type: 'owner'
+ }
+ })
+ if (loadingAddProject) return (Loading
)
+ if (newProject.errors) {
+ console.log(`${Object.keys(newProject.errors[0].extensions.exception.fields)[0]} already exists`)
+ } else {
+ const newPermission = await createPermission({
+ variables: {
+ contributor_id: sessionUser().id,
+ project_id: newProject.data.createProject.id,
+ type: 'owner'
+ }
+ })
+ }
+ return newProject
+ }
+
+ const handleAlertClose = (event, reason) => {
+ if (reason === 'clickaway') {
+ return
+ }
+ setErrorMessage('')
+ }
+
+ const saveAndContinue = async () => {
+ try {
+ const newProject = await createProject()
+ setNewProject(newProject.data.createProject)
+ setCurrentProjectCreationPhase(currentProjectCreationPhase + 1)
+ } catch (err) {
+
+ console.log(err)
+ setErrorMessage(`${err}`)
+ }
+ }
+
+ const saveProjectFunding = async () => {
+ try {
+ const fundProjectVariables = {
+ project_id: newProject.id,
+ expected_budget: budgetRange,
+ expected_budget_timeframe: timeframeAmount,
+ expected_budget_currency: currency.name
+ }
+ await addProjectFunding({
+ variables: fundProjectVariables
+ })
+ history.push('/dashboard')
+ history.go(0)
+ } catch (err) {
+
+ console.log(err)
+ setErrorMessage(`${err}`)
+ }
+ }
+
+ return (
+
+
+ {!currentProjectCreationPhase &&
+ <>
+
+
+
+
+
+
+
+ >
+ }
+ {!!currentProjectCreationPhase &&
+ <>
+
+
+ {errorMessage}
+
+
+
+
+
+
+
+ >
+ }
+
+
+ {`${errorMessage}`}
+
+
+
+ )
+}
+
+export default CreateProjectPage
\ No newline at end of file
diff --git a/srcv2/pages/DashboardPage.js b/srcv2/pages/DashboardPage.js
new file mode 100644
index 00000000..b14a50b0
--- /dev/null
+++ b/srcv2/pages/DashboardPage.js
@@ -0,0 +1,93 @@
+import React from 'react'
+import { useHistory } from 'react-router-dom';
+import {
+ Icon
+} from '@material-ui/core'
+
+import AllocationsList from '../components/AllocationsList'
+import CreateProjectFloatingButton from '../components/CreateProjectFloatingButton'
+import ProjectsList from '../components/ProjectsList'
+import Section from '../components/Section'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { getHandle } from '../scripts/selectors'
+
+const DashboardPage = () => {
+
+ const history = useHistory()
+
+ const userWallet = sessionUser().wallet
+
+ return (
+
+
+
+
+ {`Welcome, @${getHandle(sessionUser().github_handle)}`}
+
+
+
+
+ {/* DEPRECATED
+
+ {!!sessionUser().totalPaid &&
+ <>
+
My allocations
+
{`${sessionUser().totalPaid} units`}
+ >
+ }
+ {!sessionUser().totalPaid &&
+
+ }
+
+ {!!sessionUser().totalPaid &&
+
+
+
+ } */}
+
+
+
+
+
+ )
+}
+
+export default DashboardPage
\ No newline at end of file
diff --git a/srcv2/pages/EditPaymentPage.js b/srcv2/pages/EditPaymentPage.js
new file mode 100644
index 00000000..db250787
--- /dev/null
+++ b/srcv2/pages/EditPaymentPage.js
@@ -0,0 +1,230 @@
+import React, { useEffect, useState } from 'react'
+import { useHistory, useParams } from 'react-router-dom'
+import { useQuery, useMutation } from '@apollo/client'
+import CurrencyTextField from '@unicef/material-ui-currency-textfield'
+import moment from 'moment'
+import {
+ MuiPickersUtilsProvider,
+ KeyboardDatePicker
+} from '@material-ui/pickers'
+import MomentUtils from '@date-io/moment'
+
+import BtcInvoiceModal from '../components/BtcInvoiceModal'
+import Section from '../components/Section'
+
+import { GET_PAYMENT_DETAILS } from '../operations/queries/PaymentQueries'
+import { CREATE_BITCOIN_INVOICE, UPDATE_PAYMENT } from '../operations/mutations/PaymentMutations'
+
+import { selectCurrencyInformation } from '../scripts/selectors'
+
+const EditPaymentPage = (props) => {
+
+ const { paymentId } = useParams()
+ const history = useHistory()
+
+ const [paymentAmount, setPaymentAmount] = useState()
+ const [paymentCurrency, setPaymentCurrency] = useState()
+ const [dateIncurred, setDateIncurred] = useState()
+ const [datePaid, setDatePaid] = useState()
+ const [bitcoinCheckoutUrl, setBitcoinCheckoutUrl] = useState()
+ const [isBitcoinInvoiceExpired, setIsBitcoinInvoiceExpired] = useState(false)
+ const [openInvoice, setOpenInvoice] = useState(false)
+ const [errorMessage, setErrorMessage] = useState('')
+
+ const {
+ loading: paymentDetailsLoading,
+ error: paymentDetailsError,
+ data: paymentDetailsData
+ } = useQuery(GET_PAYMENT_DETAILS, {
+ variables: {
+ paymentId: Number(paymentId)
+ },
+ onCompleted: paymentData => {
+ const { getPaymentById: payment } = paymentData
+ setPaymentAmount(payment.amount)
+ setPaymentCurrency(payment.currency)
+ setDateIncurred(moment.utc(payment.date_incurred, 'x').format('YYYY-MM-DD'))
+ setDatePaid(payment.date_paid
+ ? moment.utc(payment.date_paid, 'x').format('YYYY-MM-DD')
+ : null)
+ }
+ })
+
+ const [updatePayment, {
+ dataPayment,
+ loading: loadingPayment,
+ errorPayment
+ }] = useMutation(UPDATE_PAYMENT)
+
+ const [generateBitcoinInvoice, {
+ dataInvoice,
+ loading: loadingInvoice,
+ errorInvoice
+ }] = useMutation(CREATE_BITCOIN_INVOICE, {
+ refetchQueries: [{
+ query: GET_PAYMENT_DETAILS,
+ variables: {
+ paymentId: Number(paymentId)
+ },
+ awaitReftechQueries: true
+ }]
+ })
+
+ if (paymentDetailsLoading) return 'Loading...'
+ if (paymentDetailsError) return `${paymentDetailsError}`
+
+ const { getPaymentById: payment } = paymentDetailsData
+
+ const currencyInformation = payment.currency
+ ? selectCurrencyInformation({
+ currency: payment.currency
+ })
+ : null
+
+ const handleDateIncurredChange = (date) => {
+ if (date) {
+ setDateIncurred(moment(date['_d']).format('YYYY-MM-DD'))
+ } else {
+ setDateIncurred(null)
+ }
+ }
+
+ const handleDatePaidChange = (date) => {
+ if (date) {
+ setDatePaid(moment(date['_d']).format('YYYY-MM-DD'))
+ } else {
+ setDatePaid(null)
+ }
+ }
+
+ const handlePaymentUpdate = async () => {
+ const variables = {
+ id: Number(paymentId),
+ amount: Number(paymentAmount),
+ date_incurred: dateIncurred,
+ date_paid: datePaid != 'Invalid date' ? datePaid : null
+ }
+ try {
+ await updatePayment({ variables })
+ // history.push(`/projects/${payment.project_id}`)
+ } catch (error) {
+ setErrorMessage('Error Updating Payment: ' + error)
+ console.log('Error Updating Payment ' + error)
+ }
+ }
+
+ const handleBitcoinInvoiceGeneration = async () => {
+ try {
+ const bitcoinInvoice = await generateBitcoinInvoice({
+ variables: {
+ paymentId: Number(paymentId)
+ }
+ })
+ if (!loadingInvoice && !errorInvoice) {
+ setBitcoinCheckoutUrl(bitcoinInvoice.data.generateBitcoinInvoiceFromPayment.bitcoinCheckoutUrl);
+ setIsBitcoinInvoiceExpired(false)
+ setOpenInvoice(true)
+ }
+ } catch (error) {
+ setErrorMessage('An error ocurred ' + error)
+ console.log('An error ocurred ' + error)
+ }
+ }
+
+ const handleViewBitcoinInvoice = () => {
+ if (!isBitcoinInvoiceExpired) setOpenInvoice(true)
+ else {
+ console.log('Bitcoin Invoice has expired')
+ }
+ }
+
+ return (
+
+
+
+
Edit payment
+
Edit the details of the payment below
+
setPaymentAmount(value)}
+ />
+
+
+
+
+
+
+
+ {(payment.currency === 'BTC' || payment.currency === 'SATS') &&
+
+ }
+ {bitcoinCheckoutUrl &&
+
+ }
+
+
+
+ {errorMessage}
+
+
+
setOpenInvoice(false)}
+ bitcoinCheckoutUrl={bitcoinCheckoutUrl}
+ />
+
+ )
+}
+
+export default EditPaymentPage
\ No newline at end of file
diff --git a/srcv2/pages/LandingPage.js b/srcv2/pages/LandingPage.js
new file mode 100644
index 00000000..38406b85
--- /dev/null
+++ b/srcv2/pages/LandingPage.js
@@ -0,0 +1,93 @@
+import React from 'react'
+
+import GitHubButton from '../components/GitHubButton'
+import Section from '../components/Section'
+import Footer from '../components/Footer'
+
+import {
+ BUDGETING_IMAGE_URL,
+ HERO_IMAGE_URL,
+ IPHONE_IMAGE_URL
+} from '../constants'
+
+const LandingPage = () => {
+
+ const renderFeatures = () => {
+ const FEATURES = [
+ {
+ title: 'Project Administration',
+ goals: ['Create projects from any Github repo', 'View insights for a project'],
+ imageUrl: IPHONE_IMAGE_URL,
+ imageHeight: 'h-'
+ },
+ {
+ title: 'Budgeting',
+ goals: ['Allocate funds to your team', 'Negotiate compensation rates', 'Fund allocations with BTC'],
+ imageUrl: BUDGETING_IMAGE_URL
+ },
+ {
+ title: 'Bitcoin Bonuses',
+ goals: [],
+ imageUrl: 'https://project-trinary.s3.us-east-1.amazonaws.com/images/btc-landing-mockup.png'
+ }
+ ]
+ return FEATURES.map((feature, idx) => {
+ const goals = feature.goals.map(goal => {
+ return (
+
+ {goal}
+
+ )
+ })
+ return (
+
+
+
+ {feature.title}
+
+ {goals}
+
+
![{feature.title}]({feature.imageUrl})
+
+
+ )
+ })
+ }
+
+ return (
+
+
+
+
+
+ Trinary
+
+
+ Build a “micro-economy” for your team to share knowledge, trade feedback, & hold each other accountable
+
+
+
+
![landing]({HERO_IMAGE_URL})
+
+
+
+
+
+
+ { renderFeatures() }
+
+
+
+
+ )
+}
+
+export default LandingPage
\ No newline at end of file
diff --git a/srcv2/pages/LoginPage.js b/srcv2/pages/LoginPage.js
new file mode 100644
index 00000000..27e8e154
--- /dev/null
+++ b/srcv2/pages/LoginPage.js
@@ -0,0 +1,46 @@
+import React from 'react'
+
+import GitHubButton from '../components/GitHubButton'
+import Section from '../components/Section'
+
+import { HERO_IMAGE_URL } from '../constants'
+
+const LoginPage = () => {
+
+ return (
+
+
+
+
+
+ Log in
+
+
+ A budgeting tool for tracking workflows and cashflows while collaborating with others on projects
+
+
+
+
![landing]({HERO_IMAGE_URL})
+
+
+
+
+
+ )
+}
+
+export default LoginPage
\ No newline at end of file
diff --git a/srcv2/pages/OnboardingContributorPage.js b/srcv2/pages/OnboardingContributorPage.js
new file mode 100644
index 00000000..71bd3d54
--- /dev/null
+++ b/srcv2/pages/OnboardingContributorPage.js
@@ -0,0 +1,46 @@
+import React, { useEffect, useState } from 'react'
+
+import BudgetingOnboarding from '../components/BudgetingOnboarding'
+import CreateProjectOnboarding from '../components/CreateProjectOnboarding'
+import PlanFundingOnboarding from '../components/PlanFundingOnboarding'
+import ProjectAdministrationOnboarding from '../components/ProjectAdministrationOnboarding'
+import Section from '../components/Section'
+
+const OnboardingContributorPage = () => {
+
+ const [currentSectionIdx, setCurrentSectionIdx] = useState(0)
+ const [projectCreated, setProjectCreated] = useState(null)
+
+ const goToNextSection = () => {
+ setCurrentSectionIdx(currentSectionIdx + 1)
+ }
+
+ const sections = [
+ ,
+ ,
+ ,
+
+ ]
+
+ const renderCurrentSection = () => {
+ return (sections[currentSectionIdx])
+ }
+
+ return (
+
+ {renderCurrentSection()}
+
+ )
+}
+
+export default OnboardingContributorPage
\ No newline at end of file
diff --git a/srcv2/pages/OnboardingPage.js b/srcv2/pages/OnboardingPage.js
new file mode 100644
index 00000000..e38a9c47
--- /dev/null
+++ b/srcv2/pages/OnboardingPage.js
@@ -0,0 +1,54 @@
+import React from 'react'
+import { useHistory, Link } from 'react-router-dom'
+
+import Section from '../components/Section'
+
+const OnboardingPage = () => {
+
+ const history = useHistory()
+
+ const fundingPrototypeLink = 'https://www.figma.com/proto/qgGWXmprU7vTv7guzWzvML/Project-Trinary?node-id=4554%3A16170&scaling=scale-down&page-id=4076%3A12706&starting-point-node-id=4554%3A16170&show-proto-sidebar=1'
+
+ return (
+
+
+
+
+
+ Choose one option
+
+
+ Create your own project from an existing Github repo or explore public projects to fund yourself
+
+
+
+
![onboarding](https://project-trinary.s3.us-east-1.amazonaws.com/images/onboarding-hero.png)
+
+
+
+
+
+
history.push('/dashboard')}>
+ Skip
+
+
+
+
+
+
+ )
+}
+
+export default OnboardingPage
\ No newline at end of file
diff --git a/srcv2/pages/ProjectDetailPage.js b/srcv2/pages/ProjectDetailPage.js
new file mode 100644
index 00000000..aaf56129
--- /dev/null
+++ b/srcv2/pages/ProjectDetailPage.js
@@ -0,0 +1,299 @@
+import React, { useEffect, useState } from 'react'
+import { useParams, useHistory } from 'react-router-dom'
+import { useQuery } from '@apollo/client'
+import moment from 'moment'
+import { Icon } from '@material-ui/core'
+
+import AddContributorList from '../components/AddContributorList'
+import CreatePaymentFloatingBadge from '../components/CreatePaymentFloatingBadge'
+import OpenInGithubButton from '../components/OpenInGithubButton'
+import Overlay from '../components/Overlay'
+import Section from '../components/Section'
+import SendBonus from '../components/SendBonus'
+
+import { GET_PROJECT } from '../operations/queries/ProjectQueries'
+
+import { getHandle, selectCurrencyInformation } from '../scripts/selectors'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { TEMP_AUTHORIZED_SUPERUSER_ID } from '../constants'
+
+const ProjectDetailPage = (props) => {
+
+ const { projectId } = useParams()
+ const history = useHistory()
+
+ const [openBonusesOverlay, setOpenBonusesOverlay] = useState(false)
+ const [openAddContributorOverlay, setOpenAddContributorOverlay] = useState(false)
+ const [screenIndex, setScreenIndex] = useState(0)
+
+ const isSuperUser = process.env.REACT_APP_TEMP_AUTHORIZED_SUPERUSER_ID
+
+ const {
+ data: dataProject,
+ loading: loadingProject,
+ error: errorProject,
+ } = useQuery(GET_PROJECT, {
+ variables: {
+ id: Number(projectId)
+ }
+ })
+
+ useEffect(() => {
+ if (!openBonusesOverlay && !screenIndex) {
+ setScreenIndex(0)
+ }
+ }, [openBonusesOverlay])
+
+ if (loadingProject) return ('Loading...')
+
+ if (errorProject) return (`${errorProject}`)
+
+ const project = dataProject.getProjectById
+
+ const renderContributors = (contributors) => {
+ return contributors.map(contributor => {
+ return (
+
+ )
+ })
+ }
+
+ const renderPayments = (payments) => {
+ return payments.map(payment => {
+ return (
+
+
+
+ {payment.contributor &&
+ <>
+
+ {payment.contributor.name}
+
+
+ {`@${getHandle(payment.contributor.github_handle)}`}
+
+ >
+ }
+
+
+
+ {`${selectCurrencyInformation({ currency: payment.currency }).symbol} ${payment.amount}`}
+
+
+
+
+
+ )
+ })
+ }
+
+ return (
+
+
+
+
+ Overview
+
+
+
+
+
+
+
+ {project.name.charAt(0).toUpperCase()}
+
+
+
+
+
+
+
+ {project.expected_budget_currency}
+
+
+
+
+
+ {project.expected_budget &&
+
+
+
+
{`${project.expected_budget_currency ?? '$'} ${project.expected_budget}`}
+
+
+ }
+
+
+
+
{`${project.expected_budget_currency ?? '$'} ${project.totalPaid ?? 0}`}
+
+
+ {project.date &&
+
+
+
{`${moment(project.date, 'x').format('YYYY-MM-DD')}`}
+
+ }
+ {project.end_date &&
+
+
+
{`${moment(project.end_date, 'x').format('YYYY-MM-DD')}`}
+
+ }
+
+
+
+
+
+
+
+
+ Active Contributors
+
+
+ {sessionUser().id == isSuperUser &&
+
+ }
+
+
+ {renderContributors(project.contributors)}
+ {!project.contributors.length &&
+
+ No active contributors at the moment
+
+ }
+
+
+
+
+
+
+
+
+ Issues
+
+
+
+
+
+
{project.githubIssuesOpened}
+
+
+
+
{project.githubIssuesClosed}
+
+
+
+
+
+
+
{project.githubPullRequestsOpened}
+
+
+
+
{project.githubPullRequestsMerged}
+
+
+
+
{project.githubPullRequestsClosed}
+
+
+
+
+
+
+
+ Payments
+
+
+ {renderPayments(project.payments)}
+
+
+
+ setScreenIndex(screenIndex - 1)}
+ >
+
+
+
+
+
+
+ )
+}
+
+export default ProjectDetailPage
\ No newline at end of file
diff --git a/srcv2/pages/WalletSetupPage.js b/srcv2/pages/WalletSetupPage.js
new file mode 100644
index 00000000..4f9e32d6
--- /dev/null
+++ b/srcv2/pages/WalletSetupPage.js
@@ -0,0 +1,68 @@
+import React, { useEffect } from 'react'
+import { useQuery } from '@apollo/client'
+import { Icon } from '@material-ui/core'
+import { useHistory } from 'react-router-dom'
+
+import Section from '../components/Section'
+import WalletOption from '../components/WalletOption'
+import { WALLET_OPTIONS } from '../constants'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { GET_CONTRIBUTOR_WALLETS } from '../operations/queries/ContributorQueries'
+
+const WalletSetupPage = () => {
+
+ const {
+ data: dataContributorWallets,
+ loading: loadingContributorWallets,
+ error: errorContributorWallets
+ } = useQuery(GET_CONTRIBUTOR_WALLETS, {
+ variables: {
+ id: Number(sessionUser().id)
+ }
+ })
+
+ const history = useHistory()
+
+ const renderWalletOptions = () => {
+ const contributorWallets = dataContributorWallets.getContributorById.wallet
+
+ return WALLET_OPTIONS.map((w, i) => {
+
+ const isCompleted = contributorWallets ? (contributorWallets[w.attribute] != null && contributorWallets[w.attribute] != '') : false
+ return (
+
+ )
+ })
+ }
+
+ if (!dataContributorWallets) { return ('Loading...') }
+
+ return (
+
+
+
+
+ {'Choose one option'}
+
+
+ {'Choose one of this three options and set up your wallet'}
+
+ {renderWalletOptions()}
+
+
+ )
+}
+
+export default WalletSetupPage
\ No newline at end of file
diff --git a/srcv2/pages/WalletSimpleSetupPage.js b/srcv2/pages/WalletSimpleSetupPage.js
new file mode 100644
index 00000000..b603ffd9
--- /dev/null
+++ b/srcv2/pages/WalletSimpleSetupPage.js
@@ -0,0 +1,160 @@
+import React, { useEffect, useState } from 'react'
+import { useMutation, useQuery } from '@apollo/client'
+import {
+ Icon,
+ Snackbar
+} from '@material-ui/core'
+import Alert from '@material-ui/lab/Alert'
+import { useHistory } from 'react-router-dom'
+
+import Overlay from '../components/Overlay'
+import Section from '../components/Section'
+import Step from '../components/Step'
+import WalletSimpleSetupOnboarding from '../components/WalletSimpleSetupOnboarding'
+
+import { sessionUser } from '../reactivities/variables'
+
+import { UPDATE_WALLET_ADDRESS } from '../operations/mutations/WalletMutations'
+
+import { GET_CONTRIBUTOR_WALLETS } from '../operations/queries/ContributorQueries'
+
+const WalletSimpleSetupPage = () => {
+
+ const [btcAddress, setBtcAddress] = useState(sessionUser().wallet ? sessionUser().wallet.onchain_address : '')
+ const [displayAlert, setDisplayAlert] = useState(false)
+ const [onboardOverlayOpen, setOnboardOverlayOpen] = useState(false)
+ const [onboardingScreenIndex, setOnboardingScreenIndex] = useState(0)
+
+ const openOnboardingOverlay = () => {
+ setOnboardingScreenIndex(0)
+ setOnboardOverlayOpen(!onboardOverlayOpen)
+ }
+
+ const history = useHistory()
+
+ const [
+ updateWalletAddress,
+ {
+ data: updateWalletAddressData,
+ loading: updateWalletAddressLoading,
+ error: updateWalletAddressError
+ }
+ ] = useMutation(UPDATE_WALLET_ADDRESS, {
+ errorPolicy: 'all',
+ refetchQueries: [{
+ query: GET_CONTRIBUTOR_WALLETS,
+ variables: {
+ id: Number(sessionUser().id)
+ }
+ }],
+ })
+
+ useEffect(() => {
+ if (updateWalletAddressError != undefined || updateWalletAddressData != undefined) {
+ setDisplayAlert(true)
+ }
+ }, [updateWalletAddressError, updateWalletAddressData])
+
+ const handleAlertClose = (event, reason) => {
+ if (reason === 'clickaway') {
+ return
+ }
+ setDisplayAlert(false)
+ }
+
+ const updateAddress = async (address) => {
+ const variables = {
+ address: address
+ }
+ await updateWalletAddress({ variables: variables })
+ }
+
+ return (
+
+
+
+ Simple Wallet Setup
+
+
+
+ Enter your info below to set up your wallet
+
+
+ setOnboardOverlayOpen(true)}
+ />
+
+
+
+ setBtcAddress(e.target.value)}
+ className='
+ form-control
+ block
+ w-full
+ px-3
+ py-1.5
+ text-black
+ font-normal
+ bg-white bg-clip-padding
+ border border-solid border-light
+ rounded-lg
+ transition
+ ease-in-out
+ m-0
+ focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none
+ '
+ />
+
+
+
+
+
+
+
+ {updateWalletAddressError ? (
+
+ {`${updateWalletAddressError.message}`}
+
+ ) : (
+
+ {`Wallet updated`}
+
+ )}
+
+
setOnboardingScreenIndex(onboardingScreenIndex - 1)}
+ >
+
+
+
+
+ )
+}
+
+export default WalletSimpleSetupPage
diff --git a/srcv2/reactivities/variables.js b/srcv2/reactivities/variables.js
new file mode 100644
index 00000000..67ec7c65
--- /dev/null
+++ b/srcv2/reactivities/variables.js
@@ -0,0 +1,5 @@
+import { makeVar } from '@apollo/client'
+
+export const authUser = makeVar(false)
+export const sessionUser = makeVar({})
+export const pageName = makeVar('')
\ No newline at end of file
diff --git a/srcv2/scripts/selectors.js b/srcv2/scripts/selectors.js
new file mode 100644
index 00000000..e325a54b
--- /dev/null
+++ b/srcv2/scripts/selectors.js
@@ -0,0 +1,9 @@
+import { CURRENCIES } from '../constants'
+
+export const getHandle = (url) => {
+ return url.split('/').pop()
+}
+
+export const selectCurrencyInformation = (props) => {
+ return CURRENCIES.find(c => c.name == props.currency)
+}
\ No newline at end of file
diff --git a/srcv2/styles/AddPaymentPage.scss b/srcv2/styles/AddPaymentPage.scss
new file mode 100644
index 00000000..434e624d
--- /dev/null
+++ b/srcv2/styles/AddPaymentPage.scss
@@ -0,0 +1,27 @@
+.MuiFormLabel-root.Mui-focused {
+ color: #00C2D4;
+}
+
+.MuiPickersDay-daySelected {
+ background-color: #00C2D4 !important;
+}
+
+.MuiPickersDay-current {
+ color: #E2E2E2 !important;
+}
+
+.Mui-focused {
+
+ .MuiOutlinedInput-notchedOutline {
+ border-color: #00C2D4 !important;
+ }
+
+}
+
+.Mui-focused {
+ border-color: #00C2D4 !important;
+}
+
+.MuiInput-underline:after {
+ border-color: #00C2D4 !important;
+}
\ No newline at end of file
diff --git a/srcv2/styles/AllocationTile.scss b/srcv2/styles/AllocationTile.scss
new file mode 100644
index 00000000..50bc77c0
--- /dev/null
+++ b/srcv2/styles/AllocationTile.scss
@@ -0,0 +1,9 @@
+.AllocationTile {
+ .allocation-options {
+ .icon {
+ text-align: center;
+ font-size: 13px;
+ width: inherit;
+ }
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/AllocationsList.scss b/srcv2/styles/AllocationsList.scss
new file mode 100644
index 00000000..379a9567
--- /dev/null
+++ b/srcv2/styles/AllocationsList.scss
@@ -0,0 +1,5 @@
+.AllocationsList {
+ .icon {
+ width: auto;
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/CreatePaymentFloatingBadge.scss b/srcv2/styles/CreatePaymentFloatingBadge.scss
new file mode 100644
index 00000000..5afc3791
--- /dev/null
+++ b/srcv2/styles/CreatePaymentFloatingBadge.scss
@@ -0,0 +1,5 @@
+.CreatePaymentFloatingBadge {
+ .icon {
+ width: auto;
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/LandingPage.scss b/srcv2/styles/LandingPage.scss
new file mode 100644
index 00000000..64c9c915
--- /dev/null
+++ b/srcv2/styles/LandingPage.scss
@@ -0,0 +1,5 @@
+.LandingPage {
+ .header {
+ max-height: 640px
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/LoginPage.scss b/srcv2/styles/LoginPage.scss
new file mode 100644
index 00000000..246f6187
--- /dev/null
+++ b/srcv2/styles/LoginPage.scss
@@ -0,0 +1,5 @@
+.LoginPage {
+ .header {
+ max-height: 545px
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/Overlay.scss b/srcv2/styles/Overlay.scss
new file mode 100644
index 00000000..7789815f
--- /dev/null
+++ b/srcv2/styles/Overlay.scss
@@ -0,0 +1,14 @@
+.Overlay {
+ .bottom {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
+ .top {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/ProjectDetailPage.scss b/srcv2/styles/ProjectDetailPage.scss
new file mode 100644
index 00000000..bdfc4e81
--- /dev/null
+++ b/srcv2/styles/ProjectDetailPage.scss
@@ -0,0 +1,10 @@
+.ProjectDetailPage {
+ .icon {
+ width: fit-content;
+ }
+ .contributor {
+ .icon {
+ width: 100%;
+ }
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/WalletSimpleSetupPage.scss b/srcv2/styles/WalletSimpleSetupPage.scss
new file mode 100644
index 00000000..1ada1e3b
--- /dev/null
+++ b/srcv2/styles/WalletSimpleSetupPage.scss
@@ -0,0 +1,10 @@
+.WalletSimpleSetupPage {
+ .info {
+ display: table;
+ span {
+ display: table-cell;
+ vertical-align: middle;
+ text-align: center;
+ }
+ }
+}
\ No newline at end of file
diff --git a/srcv2/styles/colors.module.scss b/srcv2/styles/colors.module.scss
new file mode 100644
index 00000000..1914a3ac
--- /dev/null
+++ b/srcv2/styles/colors.module.scss
@@ -0,0 +1,2 @@
+:export {
+}
\ No newline at end of file
diff --git a/srcv2/styles/index.scss b/srcv2/styles/index.scss
new file mode 100644
index 00000000..f03f8cc0
--- /dev/null
+++ b/srcv2/styles/index.scss
@@ -0,0 +1,35 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ position: relative;
+ min-height: 100vh;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
+
+// Globals
+@import './colors.module.scss';
+
+//Pages
+@import './AddPaymentPage.scss';
+@import './LandingPage.scss';
+@import './LoginPage.scss';
+@import './ProjectDetailPage.scss';
+@import './WalletSimpleSetupPage.scss';
+
+//Components
+@import './AllocationsList.scss';
+@import './AllocationTile.scss';
+@import './CreatePaymentFloatingBadge.scss';
+@import './Overlay.scss'
\ No newline at end of file
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 00000000..375cc772
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,19 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: [
+ './srcv2/**/*.{js,jsx,ts,tsx}'
+ ],
+ theme: {
+ extend: {
+ colors: {
+ 'setlife': '#00C2D4',
+ 'light': '#E2E2E2',
+ 'gray': '#9BAAB9',
+ 'black': '#000000',
+ 'white-light': '#F4F4F4',
+ 'med-gray': '#E5E5E5'
+ }
+ },
+ },
+ plugins: [],
+}
\ No newline at end of file