Paul M Fox 5 years ago
parent
commit
1eaa9a0227

+ 2 - 1
packages/joy-roles/package.json

@@ -20,6 +20,7 @@
     "react-compound-slider": "^2.4.0",
     "react-moment": "^0.9.6",
     "react-number-format": "^4.3.1",
-    "react-semantic-ui-range": "^0.7.0"
+    "react-semantic-ui-range": "^0.7.0",
+    "typescript-formatter": "^7.2.2"
   }
 }

+ 27 - 27
packages/joy-roles/src/classifiers.spec.ts

@@ -2,36 +2,36 @@ import { AcceptingApplications, ActiveOpeningStage, OpeningStage, OpeningStageAc
 import { classifyOpeningStage, OpeningStageClassification } from './classifiers'
 
 type Test = {
-	description: string
-	input: OpeningStage
-	output: OpeningStageClassification
+  description: string
+  input: OpeningStage
+  output: OpeningStageClassification
 }
 
 describe('publicToAddr', (): void => {
 
-	const cases:Test[] = [
-		{
-			description: "Accepting applications",
-			input: new OpeningStage({ 
-				openingStageActive: new OpeningStageActive({
-					stage: new ActiveOpeningStage({
-						acceptingApplications: new AcceptingApplications({
-							started_accepting_applicants_at_block: 100,
-						})
-					})
-				})
-			}),
-			output: {
-				description: "Accepting applications",
-				class: "active",
-				starting_block: 100,
-			},
-		}
-	]
+  const cases: Test[] = [
+    {
+      description: "Accepting applications",
+      input: new OpeningStage({
+        openingStageActive: new OpeningStageActive({
+          stage: new ActiveOpeningStage({
+            acceptingApplications: new AcceptingApplications({
+              started_accepting_applicants_at_block: 100,
+            })
+          })
+        })
+      }),
+      output: {
+        description: "Accepting applications",
+        class: "active",
+        starting_block: 100,
+      },
+    }
+  ]
 
-	cases.forEach( (test:Test) => {
-		it(test.description, (): void => {
-			expect(classifyOpeningStage(test.input)).toEqual(test.output);
-		});
-	})
+  cases.forEach((test: Test) => {
+    it(test.description, (): void => {
+      expect(classifyOpeningStage(test.input)).toEqual(test.output);
+    });
+  })
 })

+ 17 - 17
packages/joy-roles/src/classifiers.ts

@@ -1,29 +1,29 @@
 import { OpeningStage } from "@joystream/types/hiring"
 
 export enum OpeningState {
-	WaitingToBegin = 0,
-	AcceptingApplications,
-	InReview,
-	Complete,
-	Cancelled,
+  WaitingToBegin = 0,
+  AcceptingApplications,
+  InReview,
+  Complete,
+  Cancelled,
 }
 
 export interface OpeningStageClassification {
-	uri: string
-	state: OpeningState
-	starting_block: number
-	starting_block_hash: string
-	created_time: Date
-	review_end_time?: Date
-	review_end_block?: number
+  uri: string
+  state: OpeningState
+  starting_block: number
+  starting_block_hash: string
+  created_time: Date
+  review_end_time?: Date
+  review_end_block?: number
 }
 
 export function classifyOpeningStage(stage: OpeningStage): OpeningStageClassification {
-	// TODO! Implement this properly, based on enum values
-	return {
-		state: OpeningState.AcceptingApplications,
-		starting_block: 100,
-	}
+  // TODO! Implement this properly, based on enum values
+  return {
+    state: OpeningState.AcceptingApplications,
+    starting_block: 100,
+  }
 }
 
 

+ 19 - 19
packages/joy-roles/src/elements.stories.tsx

@@ -11,9 +11,9 @@ import { BalanceView, GroupMemberView, HandleView, MemberView, MemoView } from '
 import 'semantic-ui-css/semantic.min.css'
 import '@polkadot/joy-roles/index.sass'
 
-export default { 
+export default {
   title: 'Roles / Elements',
-    decorators: [withKnobs],
+  decorators: [withKnobs],
 }
 
 export const Balance = () => {
@@ -23,7 +23,7 @@ export const Balance = () => {
 }
 
 export const Memo = () => {
-  const actor = new Actor({member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'})
+  const actor = new Actor({ member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp' })
   const memo = new Text(text("Memo text", "This is a memo"))
 
   return (
@@ -32,30 +32,30 @@ export const Memo = () => {
 }
 
 export const Handle = () => {
-  const profile = { 
-    handle: new Text(text("Handle","benholdencrowther")),
+  const profile = {
+    handle: new Text(text("Handle", "benholdencrowther")),
   }
 
   return (
     <HandleView profile={profile} />
-      )
+  )
 }
 
 export const Member = () => {
-  const actor = new Actor({member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'})
-  const profile = { 
-    handle: new Text(text("Handle","benholdencrowther")),
+  const actor = new Actor({ member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp' })
+  const profile = {
+    handle: new Text(text("Handle", "benholdencrowther")),
   }
 
   return (
-        <Table basic='very'>
+    <Table basic='very'>
       <Table.Body>
         <Table.Row>
           <Table.Cell>
-            <MemberView 
-              actor={actor} 
-              balance={new u128(number('Balance', 10))} 
-              profile={profile} 
+            <MemberView
+              actor={actor}
+              balance={new u128(number('Balance', 10))}
+              profile={profile}
             />
           </Table.Cell>
         </Table.Row>
@@ -65,15 +65,15 @@ export const Member = () => {
 }
 
 export const GroupMember = () => {
-  const actor = new Actor({member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'})
-  const profile = { 
-    handle: new Text(text("Handle","benholdencrowther")),
+  const actor = new Actor({ member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp' })
+  const profile = {
+    handle: new Text(text("Handle", "benholdencrowther")),
   }
 
   return (
-    <GroupMemberView 
+    <GroupMemberView
       actor={actor}
-      profile={profile} 
+      profile={profile}
       title={text('Title', 'Group lead')}
       lead={boolean('Lead member', true)}
       stake={new u128(number('Stake', 10))}

+ 124 - 124
packages/joy-roles/src/elements.tsx

@@ -11,118 +11,118 @@ import { Profile } from '@joystream/types/members';
 import { Text } from '@polkadot/types';
 
 type ActorProps = {
-    actor: Actor
+  actor: Actor
 }
 
 type BalanceProps = {
-    balance?: Balance
+  balance?: Balance
 }
 
 export function BalanceView(props: BalanceProps) {
-    return (
-        <div className="balance">
-            <span>Balance:</span> {formatBalance(props.balance)}
-        </div>
-    )
+  return (
+    <div className="balance">
+      <span>Balance:</span> {formatBalance(props.balance)}
+    </div>
+  )
 }
 
 type MemoProps = ActorProps & {
-    memo?: Text
+  memo?: Text
 }
 
 export function MemoView(props: MemoProps) {
-	if (typeof props.memo === "undefined") {
-		return null
-	}
-
-    return (
-        <div className="memo">
-            <span>Memo:</span> {props.memo.toString()}
-				<Link to={`#/addressbook/memo/${props.actor.account.toString()}`}>{' view full memo'}</Link>
-        </div>
-    )
+  if (typeof props.memo === "undefined") {
+    return null
+  }
+
+  return (
+    <div className="memo">
+      <span>Memo:</span> {props.memo.toString()}
+      <Link to={`#/addressbook/memo/${props.actor.account.toString()}`}>{' view full memo'}</Link>
+    </div>
+  )
 }
 
 type ProfileProps = {
-	profile: Profile
+  profile: Profile
 }
 
 export function HandleView(props: ProfileProps) {
-	if (typeof props.profile === "undefined") {
-		return null
-	}
+  if (typeof props.profile === "undefined") {
+    return null
+  }
 
-    return (
-		<Link to={`#/members/${props.profile.handle.toString()}`}>{props.profile.handle.toString()}</Link>
-    )
+  return (
+    <Link to={`#/members/${props.profile.handle.toString()}`}>{props.profile.handle.toString()}</Link>
+  )
 }
 
 type MemberProps = ActorProps & BalanceProps & ProfileProps
 
 export function MemberView(props: MemberProps) {
-	let avatar = <Identicon value={props.actor.account.toString()} size="50"  />
-	if (typeof props.profile.avatar_uri !== "undefined") {
-		avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
-	}
-
-    return (
-		<Header as='h4' image>
-			{avatar}
-			<Header.Content>
-				<HandleView profile={props.profile} />
-				<BalanceView balance={props.balance} />
-			</Header.Content>
-	    </Header>
-    )
+  let avatar = <Identicon value={props.actor.account.toString()} size="50" />
+  if (typeof props.profile.avatar_uri !== "undefined") {
+    avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
+  }
+
+  return (
+    <Header as='h4' image>
+      {avatar}
+      <Header.Content>
+        <HandleView profile={props.profile} />
+        <BalanceView balance={props.balance} />
+      </Header.Content>
+    </Header>
+  )
 }
 
 type ActorDetailsProps = MemoProps & BalanceProps
 
 export function ActorDetailsView(props: ActorDetailsProps) {
-    return (
-        <div className="actor-summary" id={props.actor.account.toString()}>
-            {props.actor.account.toString()}
-            <MemoView actor={props.actor} memo={props.memo} />
-        </div>
-    )
+  return (
+    <div className="actor-summary" id={props.actor.account.toString()}>
+      {props.actor.account.toString()}
+      <MemoView actor={props.actor} memo={props.memo} />
+    </div>
+  )
 }
 
 export type GroupMemberProps = {
-	actor: Actor
-	profile: Profile
-	title: string
-	lead: boolean
-	stake?: Balance 	
-	earned?: Balance
-	inset?: boolean
+  actor: Actor
+  profile: Profile
+  title: string
+  lead: boolean
+  stake?: Balance
+  earned?: Balance
+  inset?: boolean
 }
 
 export function GroupMemberView(props: GroupMemberProps) {
   let fluid = false
-	if (typeof props.inset !== "undefined") {
+  if (typeof props.inset !== "undefined") {
     fluid = props.inset
   }
 
-	let stake = null
-	if (typeof props.stake !== "undefined" && props.stake.toNumber() !== 0) {
+  let stake = null
+  if (typeof props.stake !== "undefined" && props.stake.toNumber() !== 0) {
     stake = (
       <Label color={props.lead ? 'teal' : 'green'} ribbon={fluid ? 'right' : 'left'}>
-        <Icon name="shield" /> 
-        Staked 
+        <Icon name="shield" />
+        Staked
         <Label.Detail>{formatBalance(props.stake)}</Label.Detail>
       </Label>
     )
-	}
+  }
 
-	let avatar = <Identicon value={props.actor.account.toString()} size="50"  />
-	if (typeof props.profile.avatar_uri !== "undefined") {
-		avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
-	}
+  let avatar = <Identicon value={props.actor.account.toString()} size="50" />
+  if (typeof props.profile.avatar_uri !== "undefined") {
+    avatar = <Image src={props.profile.avatar_uri.toString()} circular className='avatar' />
+  }
 
   let earned = null
-  if (typeof props.earned !== "undefined" && 
-      props.earned.toNumber() > 0 &&
-      !fluid) {
+  if (typeof props.earned !== "undefined" &&
+    props.earned.toNumber() > 0 &&
+    !fluid) {
     earned = (
       <Card.Content extra>
         <Label>Earned <Label.Detail>{formatBalance(props.earned)}</Label.Detail></Label>
@@ -130,76 +130,76 @@ export function GroupMemberView(props: GroupMemberProps) {
     )
   }
 
-	return (
+  return (
     <Card color={props.lead ? 'teal' : 'grey'} className="staked-card" fluid={fluid}>
       <Card.Content>
-		<Image floated='right'>
-			{avatar}
-		</Image>
-		<Card.Header><HandleView profile={props.profile} /></Card.Header>
+        <Image floated='right'>
+          {avatar}
+        </Image>
+        <Card.Header><HandleView profile={props.profile} /></Card.Header>
         <Card.Meta>{props.title}</Card.Meta>
-		 <Card.Description>
-			 {stake}
+        <Card.Description>
+          {stake}
         </Card.Description>
       </Card.Content>
       {earned}
     </Card>
-	)
+  )
 }
 
 type CountdownProps = {
-	end: Date
+  end: Date
 }
 
 export function Countdown(props: CountdownProps) {
-    let interval: number = -1
-
-    const [days, setDays] = useState<number | undefined>(undefined)
-    const [hours, setHours] = useState<number | undefined>(undefined)
-    const [minutes, setMinutes] = useState<number | undefined>(undefined)
-    const [seconds, setSeconds] = useState<number | undefined>(undefined)
-
-	const update = () => {
-		const then = moment(props.end)
-        const now = moment()
-        const d = moment.duration(then.diff(now))
-        setDays( d.days())
-        setHours( d.hours())
-        setMinutes( d.minutes())
-        setSeconds( d.seconds())
-	}
-
-	interval = window.setInterval(update, 1000);
-
-    useEffect(() => {
-		update()
-        return () => {
-            clearInterval(interval);
-        };
-    }, []);
-
-    if(!seconds) {
-        return null;
-    }
-    
-    return (
-        <div className='countdown wrapper'>
-			<Statistic size="tiny">
-				<Statistic.Value>{days}</Statistic.Value> 
-				<Statistic.Label>Days</Statistic.Label>
-			</Statistic>
-			<Statistic size="tiny">
-				<Statistic.Value>{hours}</Statistic.Value> 
-				<Statistic.Label>hours</Statistic.Label>
-			</Statistic>
-			<Statistic size="tiny">
-				<Statistic.Value>{minutes}</Statistic.Value> 
-				<Statistic.Label>minutes</Statistic.Label>
-			</Statistic>
-			<Statistic size="tiny">
-				<Statistic.Value>{seconds}</Statistic.Value> 
-				<Statistic.Label>seconds</Statistic.Label>
-			</Statistic>
-        </div>
-    )
+  let interval: number = -1
+
+  const [days, setDays] = useState<number | undefined>(undefined)
+  const [hours, setHours] = useState<number | undefined>(undefined)
+  const [minutes, setMinutes] = useState<number | undefined>(undefined)
+  const [seconds, setSeconds] = useState<number | undefined>(undefined)
+
+  const update = () => {
+    const then = moment(props.end)
+    const now = moment()
+    const d = moment.duration(then.diff(now))
+    setDays(d.days())
+    setHours(d.hours())
+    setMinutes(d.minutes())
+    setSeconds(d.seconds())
+  }
+
+  interval = window.setInterval(update, 1000);
+
+  useEffect(() => {
+    update()
+    return () => {
+      clearInterval(interval);
+    };
+  }, []);
+
+  if (!seconds) {
+    return null;
+  }
+
+  return (
+    <div className='countdown wrapper'>
+      <Statistic size="tiny">
+        <Statistic.Value>{days}</Statistic.Value>
+        <Statistic.Label>Days</Statistic.Label>
+      </Statistic>
+      <Statistic size="tiny">
+        <Statistic.Value>{hours}</Statistic.Value>
+        <Statistic.Label>hours</Statistic.Label>
+      </Statistic>
+      <Statistic size="tiny">
+        <Statistic.Value>{minutes}</Statistic.Value>
+        <Statistic.Label>minutes</Statistic.Label>
+      </Statistic>
+      <Statistic size="tiny">
+        <Statistic.Value>{seconds}</Statistic.Value>
+        <Statistic.Label>seconds</Statistic.Label>
+      </Statistic>
+    </div>
+  )
 }

+ 302 - 302
packages/joy-roles/src/flows/apply.elements.stories.tsx

@@ -5,23 +5,23 @@ import { Card, Container, Message } from 'semantic-ui-react'
 import { u128, GenericAccountId } from '@polkadot/types'
 import { Balance } from '@polkadot/types/interfaces';
 
-import { 
-    ApplicationDetails
+import {
+  ApplicationDetails
 } from '@joystream/types/schemas/role.schema'
-import { 
-    ConfirmStakesStage, ConfirmStakesStageProps,
-    ProgressStepsView, ProgressStepsProps, ProgressSteps,
-    ApplicationDetailsStage, ApplicationDetailsStageProps,
-    SubmitApplicationStage, SubmitApplicationStageProps,
-    DoneStage, DoneStageProps, 
-    FundSourceSelector,
-    StakeRankSelector, StakeRankSelectorProps,
-    ConfirmStakes2Up, ConfirmStakes2UpProps,
+import {
+  ConfirmStakesStage, ConfirmStakesStageProps,
+  ProgressStepsView, ProgressStepsProps, ProgressSteps,
+  ApplicationDetailsStage, ApplicationDetailsStageProps,
+  SubmitApplicationStage, SubmitApplicationStageProps,
+  DoneStage, DoneStageProps,
+  FundSourceSelector,
+  StakeRankSelector, StakeRankSelectorProps,
+  ConfirmStakes2Up, ConfirmStakes2UpProps,
 } from "./apply"
 import {
-    OpeningBodyApplicationsStatusProps,
-    ApplicationStakeRequirement, RoleStakeRequirement,
-    StakeType, 
+  OpeningBodyApplicationsStatusProps,
+  ApplicationStakeRequirement, RoleStakeRequirement,
+  StakeType,
 } from '../tabs/Opportunities'
 
 import { creator } from "../tabs/Opportunities.stories"
@@ -29,114 +29,114 @@ import { creator } from "../tabs/Opportunities.stories"
 import 'semantic-ui-css/semantic.min.css'
 import '@polkadot/joy-roles/index.sass'
 
-export default { 
-    title: 'Roles / Components / Apply flow / Elements',
-    decorators: [withKnobs],
+export default {
+  title: 'Roles / Components / Apply flow / Elements',
+  decorators: [withKnobs],
 }
 
 const applicationSliderOptions = {
-    range: true,
-    min: 0,
-    max: 20,
-    step: 1,
+  range: true,
+  min: 0,
+  max: 20,
+  step: 1,
 }
 
 const moneySliderOptions = {
-    range: true,
-    min: 0,
-    max: 1000000,
-    step: 500,
+  range: true,
+  min: 0,
+  max: 1000000,
+  step: 500,
 }
 
-const applications:OpeningBodyApplicationsStatusProps = {
-    numberOfApplications: number("Applications count", 0, applicationSliderOptions, "Role rationing policy"),
-    maxNumberOfApplications: number("Application max", 0, applicationSliderOptions, "Role rationing policy"),
-    requiredApplicationStake: new ApplicationStakeRequirement(
-        new u128(number("Application stake", 500, moneySliderOptions, "Role stakes")), 
-    ),
-    requiredRoleStake: new RoleStakeRequirement(
-        new u128(number("Role stake", 0, moneySliderOptions, "Role stakes")), 
-    ),
+const applications: OpeningBodyApplicationsStatusProps = {
+  numberOfApplications: number("Applications count", 0, applicationSliderOptions, "Role rationing policy"),
+  maxNumberOfApplications: number("Application max", 0, applicationSliderOptions, "Role rationing policy"),
+  requiredApplicationStake: new ApplicationStakeRequirement(
+    new u128(number("Application stake", 500, moneySliderOptions, "Role stakes")),
+  ),
+  requiredRoleStake: new RoleStakeRequirement(
+    new u128(number("Role stake", 0, moneySliderOptions, "Role stakes")),
+  ),
 }
 
 type TestProps = {
-    _description: string
+  _description: string
 }
 
 export function ProgressIndicator() {
-    const permutations:(ProgressStepsProps & TestProps)[] = [
-        {
-            _description: "Three steps, second active",
-            activeStep: ProgressSteps.SubmitApplication,
-            hasConfirmStep: false,
-        },
-        {
-            _description: "Four steps, first active",
-            activeStep: ProgressSteps.ConfirmStakes,
-            hasConfirmStep: true,
-        },
-        {
-            _description: "Four steps, second active",
-            activeStep: ProgressSteps.SubmitApplication,
-            hasConfirmStep: true,
-        },
-    ]
-
-    return (
-        <Container>
-            {permutations.map((permutation, key) => (
-                <Container className="outer" key={key}>
-                    <h4>{permutation._description}</h4>
-                    <Card fluid>
-                        <ProgressStepsView {...permutation} />
-                    </Card>
-                </Container>
-            ))}
+  const permutations: (ProgressStepsProps & TestProps)[] = [
+    {
+      _description: "Three steps, second active",
+      activeStep: ProgressSteps.SubmitApplication,
+      hasConfirmStep: false,
+    },
+    {
+      _description: "Four steps, first active",
+      activeStep: ProgressSteps.ConfirmStakes,
+      hasConfirmStep: true,
+    },
+    {
+      _description: "Four steps, second active",
+      activeStep: ProgressSteps.SubmitApplication,
+      hasConfirmStep: true,
+    },
+  ]
+
+  return (
+    <Container>
+      {permutations.map((permutation, key) => (
+        <Container className="outer" key={key}>
+          <h4>{permutation._description}</h4>
+          <Card fluid>
+            <ProgressStepsView {...permutation} />
+          </Card>
         </Container>
-    )
+      ))}
+    </Container>
+  )
 }
 
 export function FundSourceSelectorFragment() {
-    const [address, setAddress] = useState<AccountId>()
-    const [passphrase, setPassphrase] = useState("")
+  const [address, setAddress] = useState<AccountId>()
+  const [passphrase, setPassphrase] = useState("")
 
-    const props = {
-        transactionFee: new u128(number("Transaction fee", 500, moneySliderOptions, "Application Tx")), 
-        keypairs: [
-            {
-                shortName: "KP1",
-                accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
-                balance: new u128(23342),
-            },
-            {
-                shortName: "KP2",
-                accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'),
-                balance: new u128(993342),
-            },
-            {
-                shortName: "KP3",
-                accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'),
-                balance: new u128(242),
-            },
-        ],
-    }
+  const props = {
+    transactionFee: new u128(number("Transaction fee", 500, moneySliderOptions, "Application Tx")),
+    keypairs: [
+      {
+        shortName: "KP1",
+        accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
+        balance: new u128(23342),
+      },
+      {
+        shortName: "KP2",
+        accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'),
+        balance: new u128(993342),
+      },
+      {
+        shortName: "KP3",
+        accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'),
+        balance: new u128(242),
+      },
+    ],
+  }
 
-    return (
-        <Container className="apply-flow">
-            <Card fluid>
-                <Card.Content>
-                    <FundSourceSelector {...props} 
-                                        addressCallback={setAddress} 
-                                        passphraseCallback={setPassphrase} 
-                    />
-                </Card.Content>
-            </Card>
-            <Message info>
-                <p>Address: {address ? address.toString(): 'not set'}</p>
-                <p>Passphrase: {passphrase}</p>
-            </Message>
-        </Container>
-    )
+  return (
+    <Container className="apply-flow">
+      <Card fluid>
+        <Card.Content>
+          <FundSourceSelector {...props}
+            addressCallback={setAddress}
+            passphraseCallback={setPassphrase}
+          />
+        </Card.Content>
+      </Card>
+      <Message info>
+        <p>Address: {address ? address.toString() : 'not set'}</p>
+        <p>Passphrase: {passphrase}</p>
+      </Message>
+    </Container>
+  )
 }
 
 export function StakeRankSelectorFragment() {
@@ -145,43 +145,43 @@ export function StakeRankSelectorFragment() {
   // List of the minimum stake required to beat each rank
   const slots: Balance[] = []
   for (let i = 0; i < 10; i++) {
-    slots.push(new u128((i*100)+10+i+1))
+    slots.push(new u128((i * 100) + 10 + i + 1))
   }
 
-    const props: StakeRankSelectorProps = {
+  const props: StakeRankSelectorProps = {
     stake: stake,
     setStake: setStake,
     slots: slots,
     step: new u128(10),
-    }
+  }
 
-    return (
-        <Container className="apply-flow">
-            <Card fluid>
+  return (
+    <Container className="apply-flow">
+      <Card fluid>
         <Message info>
           <StakeRankSelector {...props} />
         </Message>
-            </Card>
+      </Card>
       <Message warning>
         Slots: {JSON.stringify(slots)}<br />
         Stake: {stake.toString()}
       </Message>
-        </Container>
-    )
+    </Container>
+  )
 }
 
 export function SelectTwoMinimumStakes() {
-    const [applicationStake, setApplicationStake] = useState(new u128(1))
-    const [roleStake, setRoleStake] = useState(new u128(2))
+  const [applicationStake, setApplicationStake] = useState(new u128(1))
+  const [roleStake, setRoleStake] = useState(new u128(2))
 
   // List of the minimum stake required to beat each rank
   const slots: Balance[] = []
   for (let i = 0; i < 20; i++) {
-    slots.push(new u128((i*100)+10+i+1))
+    slots.push(new u128((i * 100) + 10 + i + 1))
   }
 
 
-  const props: ConfirmStakes2UpProps & TestProps =  {
+  const props: ConfirmStakes2UpProps & TestProps = {
     _description: "One fixed stake (application), no limit",
     requiredApplicationStake: new ApplicationStakeRequirement(new u128(1)),
     requiredRoleStake: new RoleStakeRequirement(new u128(2)),
@@ -189,14 +189,14 @@ export function SelectTwoMinimumStakes() {
     numberOfApplications: 0,
     defactoMinimumStake: new u128(0),
     step: new u128(5),
-        slots: slots,
+    slots: slots,
     selectedApplicationStake: applicationStake, setSelectedApplicationStake: setApplicationStake,
     selectedRoleStake: roleStake, setSelectedRoleStake: setRoleStake,
   }
 
   return (
-        <Container className="apply-flow">
-            <Card fluid>
+    <Container className="apply-flow">
+      <Card fluid>
         <Card.Content>
           <ConfirmStakes2Up {...props} />
         </Card.Content>
@@ -206,148 +206,148 @@ export function SelectTwoMinimumStakes() {
 }
 
 export function StageAConfirmStakes() {
-  const permutations:(ConfirmStakesStageProps & TestProps)[] = [
-      {
-          _description: "One fixed stake (application), no limit",
-          requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
-          requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-          maxNumberOfApplications: 0,
-          numberOfApplications: 0,
-          defactoMinimumStake: new u128(0),
-          nextTransition: () => {},
-      },
-      {
-            _description: "One fixed stake (role), no limit",
+  const permutations: (ConfirmStakesStageProps & TestProps)[] = [
+    {
+      _description: "One fixed stake (application), no limit",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      maxNumberOfApplications: 0,
+      numberOfApplications: 0,
+      defactoMinimumStake: new u128(0),
+      nextTransition: () => { },
+    },
+    {
+      _description: "One fixed stake (role), no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
       requiredRoleStake: new RoleStakeRequirement(new u128(1213)),
       maxNumberOfApplications: 0,
       numberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Two fixed stakes, no limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Two fixed stakes, no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
       requiredRoleStake: new RoleStakeRequirement(new u128(10)),
       maxNumberOfApplications: 0,
       numberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "One fixed stake (application), 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "One fixed stake (application), 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
       requiredRoleStake: new RoleStakeRequirement(new u128(0)),
       maxNumberOfApplications: 20,
       numberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "One fixed stake (role), 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "One fixed stake (role), 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(456)),
       requiredRoleStake: new RoleStakeRequirement(new u128(0)),
       maxNumberOfApplications: 20,
       numberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Two fixed stakes, 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Two fixed stakes, 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
       requiredRoleStake: new RoleStakeRequirement(new u128(10)),
       maxNumberOfApplications: 20,
       numberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "One minimum stake (application), no limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "One minimum stake (application), no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast),
       requiredRoleStake: new RoleStakeRequirement(new u128(0)),
       maxNumberOfApplications: 0,
       numberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "One minimum stake (role), no limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "One minimum stake (role), no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
       requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast),
       maxNumberOfApplications: 0,
       numberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Two minimum stakes, no limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Two minimum stakes, no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast),
       requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast),
       maxNumberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Minimum application stake, fixed role stake, no limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Minimum application stake, fixed role stake, no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast),
       requiredRoleStake: new RoleStakeRequirement(new u128(10)),
       maxNumberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Minimum role stake, fixed application stake, no limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Minimum role stake, fixed application stake, no limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
       requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast),
       maxNumberOfApplications: 0,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "One minimum stake (application), 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "One minimum stake (application), 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast),
       requiredRoleStake: new RoleStakeRequirement(new u128(0)),
       maxNumberOfApplications: 0,
       numberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "One minimum stake (role), 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "One minimum stake (role), 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
       requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast),
       maxNumberOfApplications: 0,
       numberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Two minimum stakes, 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Two minimum stakes, 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast),
       requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast),
       maxNumberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Minimum application stake, fixed role stake, 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Minimum application stake, fixed role stake, 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10), StakeType.AtLeast),
       requiredRoleStake: new RoleStakeRequirement(new u128(10)),
       maxNumberOfApplications: 0,
       numberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-        {
-            _description: "Minimum role stake, fixed application stake, 20 applicant limit",
+      nextTransition: () => { },
+    },
+    {
+      _description: "Minimum role stake, fixed application stake, 20 applicant limit",
       requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
       requiredRoleStake: new RoleStakeRequirement(new u128(10), StakeType.AtLeast),
       maxNumberOfApplications: 0,
       numberOfApplications: 20,
       defactoMinimumStake: new u128(0),
-      nextTransition: () => {},
-        },
-    ]
+      nextTransition: () => { },
+    },
+  ]
 
   const keypairs = [
     {
@@ -371,12 +371,12 @@ export function StageAConfirmStakes() {
   // List of the minimum stake required to beat each rank
   const slots: Balance[] = []
   for (let i = 0; i < 20; i++) {
-    slots.push(new u128((i*100)+10+i+1))
+    slots.push(new u128((i * 100) + 10 + i + 1))
   }
 
 
   const renders = []
-    permutations.map((permutation, key) => {
+  permutations.map((permutation, key) => {
     const [applicationStake, setApplicationStake] = useState(new u128(0))
     const [roleStake, setRoleStake] = useState<Balance>(new u128(0))
     const [stakeKeyAddress, setStakeKeyAddress] = useState<AccountId>(null)
@@ -391,21 +391,21 @@ export function StageAConfirmStakes() {
     renders.push(
       (
         <Container className="outer" key={key}>
-            <h4>{key}. {permutation._description}</h4>
-            <Card fluid>
-                <ConfirmStakesStage {...permutation} 
-                      {...stakeRankSelectorProps} 
-                      keypairs={keypairs}
-                      selectedApplicationStake={applicationStake}
-                      setSelectedApplicationStake={setApplicationStake}
-                      selectedRoleStake={roleStake}
-                      setSelectedRoleStake={setRoleStake}
-                      keyAddress={stakeKeyAddress}
-                      setKeyAddress={setStakeKeyAddress}
-                      keyPassphrase={stakeKeyPassphrase}
-                      setKeyPassphrase={setStakeKeyPassphrase}
-                />
-            </Card>
+          <h4>{key}. {permutation._description}</h4>
+          <Card fluid>
+            <ConfirmStakesStage {...permutation}
+              {...stakeRankSelectorProps}
+              keypairs={keypairs}
+              selectedApplicationStake={applicationStake}
+              setSelectedApplicationStake={setApplicationStake}
+              selectedRoleStake={roleStake}
+              setSelectedRoleStake={setRoleStake}
+              keyAddress={stakeKeyAddress}
+              setKeyAddress={setStakeKeyAddress}
+              keyPassphrase={stakeKeyPassphrase}
+              setKeyPassphrase={setStakeKeyPassphrase}
+            />
+          </Card>
           <Message info>
             A: {applicationStake.toString()}, R: {roleStake.toString()}
           </Message>
@@ -414,119 +414,119 @@ export function StageAConfirmStakes() {
     )
   })
 
-    return (
-        <Container className="apply-flow">
-            {renders.map((render, key) => (
+  return (
+    <Container className="apply-flow">
+      {renders.map((render, key) => (
         <div>{render}</div>
-           ))}
-        </Container>
-    )
+      ))}
+    </Container>
+  )
 }
 
 export function StageBApplicationDetails() {
-    const [data, setData] = useState<object>({
-        "About you": {
-            "Your e-mail address": "pre-filled"
-        }
-    })
+  const [data, setData] = useState<object>({
+    "About you": {
+      "Your e-mail address": "pre-filled"
+    }
+  })
 
-    const props: ApplicationDetailsStageProps = {
-        applicationDetails: object("JSON",{
-          sections: [
+  const props: ApplicationDetailsStageProps = {
+    applicationDetails: object("JSON", {
+      sections: [
+        {
+          title: "About you",
+          questions: [
             {
-              title: "About you",
-              questions: [
-                {
-                  title: "Your name",
-                  type: "text"
-                },
-                {
-                  title: "Your e-mail address",
-                  type: "text"
-                }
-              ]
+              title: "Your name",
+              type: "text"
             },
             {
-              title: "Your experience",
-              questions: [
-                {
-                  title: "Why would you be good for this role?",
-                  type: "text area"
-                }
-              ]
+              title: "Your e-mail address",
+              type: "text"
             }
           ]
-        }, 'Application questions'),
-        data: data,
-        setData: setData,
-        nextTransition: () => { },
-    }
+        },
+        {
+          title: "Your experience",
+          questions: [
+            {
+              title: "Why would you be good for this role?",
+              type: "text area"
+            }
+          ]
+        }
+      ]
+    }, 'Application questions'),
+    data: data,
+    setData: setData,
+    nextTransition: () => { },
+  }
 
-    return (
-        <Container className="apply-flow">
-            <Card fluid>
-                <Card.Content>
-                  <ApplicationDetailsStage {...props} />
-                </Card.Content>
-                <Message info>
-                    {JSON.stringify(data)}
-                </Message>
-            </Card>
-        </Container>
-    )
+  return (
+    <Container className="apply-flow">
+      <Card fluid>
+        <Card.Content>
+          <ApplicationDetailsStage {...props} />
+        </Card.Content>
+        <Message info>
+          {JSON.stringify(data)}
+        </Message>
+      </Card>
+    </Container>
+  )
 }
 
 export function StageCSubmitApplication() {
-    const props: SubmitApplicationStageProps = {
-        nextTransition: () => {},
-        applications: applications,
-        creator: creator,
-        transactionFee: new u128(number("Transaction fee", 500, moneySliderOptions, "Application Tx")), 
-        transactionDetails: new Map<string, string>([
-            ["Extrinsic hash", "0xae6d24d4d55020c645ddfe2e8d0faf93b1c0c9879f9bf2c439fb6514c6d1292e"],
-            ["SOmething else", "abc123"],
-        ]),
-        keypairs: [
-            {
-                shortName: "KP1",
-                accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
-                balance: new u128(23342),
-            },
-            {
-                shortName: "KP2",
-                accountId: new GenericAccountId('5F5SwL7zwfdDN4UifacVrYKQVVYzoNcoDoGzmhVkaPN2ef8F'),
-                balance: new u128(993342),
-            },
-            {
-                shortName: "KP3",
-                accountId: new GenericAccountId('5HmMiZSGnidr3AhUk7hhZa7wJrvYyKEiT8cneyavA1ALkfJc'),
-                balance: new u128(242),
-            },
-        ],
-    }
+  const props: SubmitApplicationStageProps = {
+    nextTransition: () => { },
+    applications: applications,
+    creator: creator,
+    transactionFee: new u128(number("Transaction fee", 500, moneySliderOptions, "Application Tx")),
+    transactionDetails: new Map<string, string>([
+      ["Extrinsic hash", "0xae6d24d4d55020c645ddfe2e8d0faf93b1c0c9879f9bf2c439fb6514c6d1292e"],
+      ["SOmething else", "abc123"],
+    ]),
+    keypairs: [
+      {
+        shortName: "KP1",
+        accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
+        balance: new u128(23342),
+      },
+      {
+        shortName: "KP2",
+        accountId: new GenericAccountId('5F5SwL7zwfdDN4UifacVrYKQVVYzoNcoDoGzmhVkaPN2ef8F'),
+        balance: new u128(993342),
+      },
+      {
+        shortName: "KP3",
+        accountId: new GenericAccountId('5HmMiZSGnidr3AhUk7hhZa7wJrvYyKEiT8cneyavA1ALkfJc'),
+        balance: new u128(242),
+      },
+    ],
+  }
 
-    return (
-        <Container className="apply-flow">
-            <Card fluid>
-                <Card.Content>
-                    <SubmitApplicationStage {...props} />
-                </Card.Content>
-            </Card>
-        </Container>
-    )
+  return (
+    <Container className="apply-flow">
+      <Card fluid>
+        <Card.Content>
+          <SubmitApplicationStage {...props} />
+        </Card.Content>
+      </Card>
+    </Container>
+  )
 }
 
 export function StageDDone() {
-    const props: DoneStageProps = {
-        applications: applications,
-        roleKeyName: "NEW_ROLE_KEY",
-    }
+  const props: DoneStageProps = {
+    applications: applications,
+    roleKeyName: "NEW_ROLE_KEY",
+  }
 
-    return (
-        <Container className="apply-flow">
-            <Card fluid>
-                <DoneStage {...props} />
-            </Card>
-        </Container>
-    )
+  return (
+    <Container className="apply-flow">
+      <Card fluid>
+        <DoneStage {...props} />
+      </Card>
+    </Container>
+  )
 }

+ 78 - 78
packages/joy-roles/src/flows/apply.stories.tsx

@@ -6,8 +6,8 @@ import { Balance } from '@polkadot/types/interfaces';
 
 import { FlowModal } from "./apply"
 import {
-    ApplicationStakeRequirement, RoleStakeRequirement,
-    StakeType,
+  ApplicationStakeRequirement, RoleStakeRequirement,
+  StakeType,
 } from '../tabs/Opportunities'
 
 import { creator } from "../tabs/Opportunities.stories"
@@ -15,97 +15,97 @@ import { creator } from "../tabs/Opportunities.stories"
 import 'semantic-ui-css/semantic.min.css'
 import '@polkadot/joy-roles/index.sass'
 
-export default { 
-    title: 'Roles / Components / Apply flow',
-    decorators: [withKnobs],
+export default {
+  title: 'Roles / Components / Apply flow',
+  decorators: [withKnobs],
 }
 
 const applicationSliderOptions = {
-    range: true,
-    min: 0,
-    max: 20,
-    step: 1,
+  range: true,
+  min: 0,
+  max: 20,
+  step: 1,
 }
 
 const moneySliderOptions = {
-    range: true,
-    min: 0,
-    max: 1000000,
-    step: 500,
+  range: true,
+  min: 0,
+  max: 1000000,
+  step: 500,
 }
 
 export function ApplicationSandbox() {
-    // List of the minimum stake required to beat each rank
-    const slots: Balance[] = []
-    for (let i = 0; i < 20; i++) {
-        slots.push(new u128((i*100)+10+i+1))
+  // List of the minimum stake required to beat each rank
+  const slots: Balance[] = []
+  for (let i = 0; i < 20; i++) {
+    slots.push(new u128((i * 100) + 10 + i + 1))
 
-    }
-    const props = {
-        applications: {
-            numberOfApplications: number("Applications count", 0, applicationSliderOptions, "Role rationing policy"),
-            maxNumberOfApplications: number("Application max", 0, applicationSliderOptions, "Role rationing policy"),
-            requiredApplicationStake: new ApplicationStakeRequirement(
-                new u128(number("Application stake", 0, moneySliderOptions, "Role stakes")), 
-            ),
-            requiredRoleStake: new RoleStakeRequirement(
-                new u128(number("Role stake", 0, moneySliderOptions, "Role stakes")), 
-            ),
-        },
-        creator: creator,
-        transactionFee: new u128(number("Transaction fee", 500, moneySliderOptions, "Application Tx")), 
-        keypairs: [
-            {
-                shortName: "KP1",
-                accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
-                balance: new u128(23342),
-            },
-            {
-                shortName: "KP2",
-                accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'),
-                balance: new u128(993342),
-            },
+  }
+  const props = {
+    applications: {
+      numberOfApplications: number("Applications count", 0, applicationSliderOptions, "Role rationing policy"),
+      maxNumberOfApplications: number("Application max", 0, applicationSliderOptions, "Role rationing policy"),
+      requiredApplicationStake: new ApplicationStakeRequirement(
+        new u128(number("Application stake", 0, moneySliderOptions, "Role stakes")),
+      ),
+      requiredRoleStake: new RoleStakeRequirement(
+        new u128(number("Role stake", 0, moneySliderOptions, "Role stakes")),
+      ),
+    },
+    creator: creator,
+    transactionFee: new u128(number("Transaction fee", 500, moneySliderOptions, "Application Tx")),
+    keypairs: [
+      {
+        shortName: "KP1",
+        accountId: new GenericAccountId('5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'),
+        balance: new u128(23342),
+      },
+      {
+        shortName: "KP2",
+        accountId: new GenericAccountId('5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'),
+        balance: new u128(993342),
+      },
+      {
+        shortName: "KP3",
+        accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'),
+        balance: new u128(242),
+      },
+    ],
+    hasConfirmStep: true,
+    requiredApplicationStake: new ApplicationStakeRequirement(new u128(1)),
+    requiredRoleStake: new RoleStakeRequirement(new u128(2), StakeType.AtLeast),
+    maxNumberOfApplications: 0,
+    numberOfApplications: 0,
+    defactoMinimumStake: new u128(0),
+    step: new u128(5),
+    slots: slots,
+    applicationDetails: object('JSON', {
+      sections: [
+        {
+          title: "About you",
+          questions: [
             {
-                shortName: "KP3",
-                accountId: new GenericAccountId('5DBaczGTDhcHgwsZzNE5qW15GrQxxdyros4pYkcKrSUovFQ9'),
-                balance: new u128(242),
+              title: "Your name",
+              type: "text"
             },
-        ],
-        hasConfirmStep: true,
-        requiredApplicationStake: new ApplicationStakeRequirement(new u128(1)),
-        requiredRoleStake: new RoleStakeRequirement(new u128(2), StakeType.AtLeast),
-        maxNumberOfApplications: 0,
-        numberOfApplications: 0,
-        defactoMinimumStake: new u128(0),
-        step: new u128(5),
-        slots: slots,
-        applicationDetails: object('JSON', {
-          sections: [
             {
-              title: "About you",
-              questions: [
-                {
-                  title: "Your name",
-                  type: "text"
-                },
-                {
-                  title: "Your e-mail address",
-                  type: "text"
-                }
-              ]
-            },
+              title: "Your e-mail address",
+              type: "text"
+            }
+          ]
+        },
+        {
+          title: "Your experience",
+          questions: [
             {
-              title: "Your experience",
-              questions: [
-                {
-                  title: "Why would you be good for this role?",
-                  type: "text area"
-                }
-              ]
+              title: "Why would you be good for this role?",
+              type: "text area"
             }
           ]
-        }, "Application questions"),
-    }
+        }
+      ]
+    }, "Application questions"),
+  }
 
-    return <FlowModal {...props} />
+  return <FlowModal {...props} />
 }

File diff suppressed because it is too large
+ 485 - 485
packages/joy-roles/src/flows/apply.tsx


+ 20 - 20
packages/joy-roles/src/tabs.stories.tsx

@@ -5,35 +5,35 @@ import { ContentCuratorsSection, StorageProvidersSection } from './tabs/WorkingG
 import { OpportunitySandbox } from './tabs/Opportunities.stories'
 import { ApplicationSandbox } from './flows/apply.stories'
 
-export default { 
+export default {
   title: 'Roles / Pages',
-    decorators: [withKnobs],
+  decorators: [withKnobs],
 }
 
 export const RolesPage = () => {
   const tab = (
-		<Container>
-			<Container className="outer">
-				<ContentCuratorsSection />
-			</Container>
-			<Container>
-				<StorageProvidersSection />
-			</Container>
-		</Container>
-	)
+    <Container>
+      <Container className="outer">
+        <ContentCuratorsSection />
+      </Container>
+      <Container>
+        <StorageProvidersSection />
+      </Container>
+    </Container>
+  )
 
   const panes = [
-    { menuItem: 'Working groups', render: () => tab},
-    { menuItem: 'Opportunities', render: () => <OpportunitySandbox />},
-    { menuItem: 'My roles', render: () => null},
-]
+    { menuItem: 'Working groups', render: () => tab },
+    { menuItem: 'Opportunities', render: () => <OpportunitySandbox /> },
+    { menuItem: 'My roles', render: () => null },
+  ]
 
-	return (
-    <Tab menu={{ secondary: true, pointing: true }} 
-         panes={panes} 
-         defaultActiveIndex={0} 
+  return (
+    <Tab menu={{ secondary: true, pointing: true }}
+      panes={panes}
+      defaultActiveIndex={0}
     />
-	)
+  )
 }
 
 export const ApplicationLightbox = ApplicationSandbox

+ 248 - 247
packages/joy-roles/src/tabs/Opportunities.elements.stories.tsx

@@ -1,15 +1,16 @@
 import React from 'react'
-import { number,  text, withKnobs } from '@storybook/addon-knobs'
+import { number, text, withKnobs } from '@storybook/addon-knobs'
 import { Card, Container } from 'semantic-ui-react'
 
 import { u128 } from '@polkadot/types'
 
-import { openingClass, 
-		 OpeningBodyApplicationsStatus, OpeningBodyApplicationsStatusProps, 
-		 OpeningBodyReviewInProgress,
-		 OpeningBodyStakeRequirement, StakeRequirementProps,
-		 OpeningHeader,
-		 ApplicationStakeRequirement, RoleStakeRequirement, StakeType,
+import {
+  openingClass,
+  OpeningBodyApplicationsStatus, OpeningBodyApplicationsStatusProps,
+  OpeningBodyReviewInProgress,
+  OpeningBodyStakeRequirement, StakeRequirementProps,
+  OpeningHeader,
+  ApplicationStakeRequirement, RoleStakeRequirement, StakeType,
 } from "./Opportunities"
 import { tomorrow, yesterday } from "./Opportunities.stories"
 
@@ -18,262 +19,262 @@ import { OpeningStageClassification, OpeningState } from "../classifiers"
 import 'semantic-ui-css/semantic.min.css'
 import '@polkadot/joy-roles/index.sass'
 
-export default { 
-	title: 'Roles / Components / Opportunities groups tab / Elements',
-	decorators: [withKnobs],
+export default {
+  title: 'Roles / Components / Opportunities groups tab / Elements',
+  decorators: [withKnobs],
 }
 
 type TestProps = {
-	_description: string
+  _description: string
 }
 
-export function OpeningHeaderByState(){
-	const stages:OpeningStageClassification[] = [
-		{
-			uri: "https://some.url/#1",
-			state: OpeningState.WaitingToBegin,
-			starting_block: 2956498,
-			starting_block_hash: "somehash",
-			created_time: yesterday(),
-		},
-		{
-			uri: "https://some.url/#1",
-			state: OpeningState.AcceptingApplications,
-			starting_block: 2956498,
-			starting_block_hash: "somehash",
-			created_time: yesterday(),
-		},
-		{
-			uri: "https://some.url/#1",
-			state: OpeningState.InReview,
-			starting_block: 102456,
-			starting_block_hash: "somehash",
-			created_time: yesterday(),
-		},
-		{
-			uri: "https://some.url/#1",
-			state: OpeningState.Complete,
-			starting_block: 10345,
-			starting_block_hash: "somehash",
-			created_time: yesterday(),
-		},
-		{
-			uri: "https://some.url/#1",
-			state: OpeningState.Cancelled,
-			starting_block: 104,
-			starting_block_hash: "somehash",
-			created_time: yesterday(),
-		},
-	]
+export function OpeningHeaderByState() {
+  const stages: OpeningStageClassification[] = [
+    {
+      uri: "https://some.url/#1",
+      state: OpeningState.WaitingToBegin,
+      starting_block: 2956498,
+      starting_block_hash: "somehash",
+      created_time: yesterday(),
+    },
+    {
+      uri: "https://some.url/#1",
+      state: OpeningState.AcceptingApplications,
+      starting_block: 2956498,
+      starting_block_hash: "somehash",
+      created_time: yesterday(),
+    },
+    {
+      uri: "https://some.url/#1",
+      state: OpeningState.InReview,
+      starting_block: 102456,
+      starting_block_hash: "somehash",
+      created_time: yesterday(),
+    },
+    {
+      uri: "https://some.url/#1",
+      state: OpeningState.Complete,
+      starting_block: 10345,
+      starting_block_hash: "somehash",
+      created_time: yesterday(),
+    },
+    {
+      uri: "https://some.url/#1",
+      state: OpeningState.Cancelled,
+      starting_block: 104,
+      starting_block_hash: "somehash",
+      created_time: yesterday(),
+    },
+  ]
 
-	return (
-		<Container>
-			{stages.map((stage, key) => (
-				<Container className={"inner opening "+openingClass(stage.state)} key={key}>
-					<Card fluid className="container">
-						<Card.Content className="header">
-							<OpeningHeader stage={stage} />
-						</Card.Content>
-					</Card>
-				</Container>
-			))}
-		</Container>
-	)
-}	
+  return (
+    <Container>
+      {stages.map((stage, key) => (
+        <Container className={"inner opening " + openingClass(stage.state)} key={key}>
+          <Card fluid className="container">
+            <Card.Content className="header">
+              <OpeningHeader stage={stage} />
+            </Card.Content>
+          </Card>
+        </Container>
+      ))}
+    </Container>
+  )
+}
 
 export function OpeningApplicationsStatusByState() {
-	const permutations:(OpeningBodyApplicationsStatusProps & TestProps)[] = [
-		{
-			_description: "No limit, no applications, no stake",
-			numberOfApplications: 0,
-			maxNumberOfApplications: 0,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "No limit, some applications, no stake",
-			numberOfApplications: 15,
-			maxNumberOfApplications: 0,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "Limit, no applications, no stake",
-			numberOfApplications: 0,
-			maxNumberOfApplications: 20,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "Limit, some applications, no stake",
-			numberOfApplications: 10,
-			maxNumberOfApplications: 20,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "Limit, full applications, no stake (application impossible)",
-			numberOfApplications: 20,
-			maxNumberOfApplications: 20,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "No limit, no applications, some stake",
-			numberOfApplications: 0,
-			maxNumberOfApplications: 0,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "No limit, some applications, some stake",
-			numberOfApplications: 15,
-			maxNumberOfApplications: 0,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "Limit, no applications, some stake",
-			numberOfApplications: 0,
-			maxNumberOfApplications: 20,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "Limit, some applications, some stake",
-			numberOfApplications: 10,
-			maxNumberOfApplications: 20,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-		{
-			_description: "Limit, full applications, some stake",
-			numberOfApplications: 20,
-			maxNumberOfApplications: 20,
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-		},
-	]
+  const permutations: (OpeningBodyApplicationsStatusProps & TestProps)[] = [
+    {
+      _description: "No limit, no applications, no stake",
+      numberOfApplications: 0,
+      maxNumberOfApplications: 0,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "No limit, some applications, no stake",
+      numberOfApplications: 15,
+      maxNumberOfApplications: 0,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "Limit, no applications, no stake",
+      numberOfApplications: 0,
+      maxNumberOfApplications: 20,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "Limit, some applications, no stake",
+      numberOfApplications: 10,
+      maxNumberOfApplications: 20,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "Limit, full applications, no stake (application impossible)",
+      numberOfApplications: 20,
+      maxNumberOfApplications: 20,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "No limit, no applications, some stake",
+      numberOfApplications: 0,
+      maxNumberOfApplications: 0,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "No limit, some applications, some stake",
+      numberOfApplications: 15,
+      maxNumberOfApplications: 0,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "Limit, no applications, some stake",
+      numberOfApplications: 0,
+      maxNumberOfApplications: 20,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "Limit, some applications, some stake",
+      numberOfApplications: 10,
+      maxNumberOfApplications: 20,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+    {
+      _description: "Limit, full applications, some stake",
+      numberOfApplications: 20,
+      maxNumberOfApplications: 20,
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(10)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+    },
+  ]
 
-	return (
-		<Container>
-			{permutations.map((permutation, key) => (
-				<Container className="outer opening" key={key}>
-					<h4>{permutation._description}</h4>
-					<Container className="main">
-						<OpeningBodyApplicationsStatus {...permutation} />
-					</Container>
-				</Container>
-			))}
-		</Container>
-	)
+  return (
+    <Container>
+      {permutations.map((permutation, key) => (
+        <Container className="outer opening" key={key}>
+          <h4>{permutation._description}</h4>
+          <Container className="main">
+            <OpeningBodyApplicationsStatus {...permutation} />
+          </Container>
+        </Container>
+      ))}
+    </Container>
+  )
 }
 
 export function OpeningApplicationsStakeRequirementByStake() {
-	const permutations:(StakeRequirementProps & TestProps)[] = [
-		{
-			_description: "No stakes required (should be empty)",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-			maxNumberOfApplications: 0,
-		},
-		{
-			_description: "App stake required; no role stake required",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(500)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-			maxNumberOfApplications: 0,
-		},
-		{
-			_description: "App stake required >; no role stake required",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(500), StakeType.AtLeast),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(0),
-			maxNumberOfApplications: 0,
-		},
-		{
-			_description: "No app stake required; role stake required",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(101)),
-			defactoMinimumStake: new u128(0),
-			maxNumberOfApplications: 0,
-		},
-		{
-			_description: "No app stake required; role stake required",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(0), StakeType.AtLeast),
-			requiredRoleStake: new RoleStakeRequirement(new u128(102)),
-			defactoMinimumStake: new u128(0),
-			maxNumberOfApplications: 0,
-		},
-		{
-			_description: ">= App stake required; role stake required",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(101), StakeType.AtLeast),
-			requiredRoleStake: new RoleStakeRequirement(new u128(102)),
-			defactoMinimumStake: new u128(0),
-			maxNumberOfApplications: 0,
-		},
-		{
-			_description: "App stake required; no role stake required; dynamic minimum > 0",
-			requiredApplicationStake: new ApplicationStakeRequirement(new u128(500)),
-			requiredRoleStake: new RoleStakeRequirement(new u128(0)),
-			defactoMinimumStake: new u128(1000),
-			maxNumberOfApplications: 20,
-		},
-	]
+  const permutations: (StakeRequirementProps & TestProps)[] = [
+    {
+      _description: "No stakes required (should be empty)",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+      maxNumberOfApplications: 0,
+    },
+    {
+      _description: "App stake required; no role stake required",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(500)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+      maxNumberOfApplications: 0,
+    },
+    {
+      _description: "App stake required >; no role stake required",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(500), StakeType.AtLeast),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(0),
+      maxNumberOfApplications: 0,
+    },
+    {
+      _description: "No app stake required; role stake required",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(101)),
+      defactoMinimumStake: new u128(0),
+      maxNumberOfApplications: 0,
+    },
+    {
+      _description: "No app stake required; role stake required",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(0), StakeType.AtLeast),
+      requiredRoleStake: new RoleStakeRequirement(new u128(102)),
+      defactoMinimumStake: new u128(0),
+      maxNumberOfApplications: 0,
+    },
+    {
+      _description: ">= App stake required; role stake required",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(101), StakeType.AtLeast),
+      requiredRoleStake: new RoleStakeRequirement(new u128(102)),
+      defactoMinimumStake: new u128(0),
+      maxNumberOfApplications: 0,
+    },
+    {
+      _description: "App stake required; no role stake required; dynamic minimum > 0",
+      requiredApplicationStake: new ApplicationStakeRequirement(new u128(500)),
+      requiredRoleStake: new RoleStakeRequirement(new u128(0)),
+      defactoMinimumStake: new u128(1000),
+      maxNumberOfApplications: 20,
+    },
+  ]
 
-	return (
-		<Container>
-			{permutations.map((permutation, key) => (
-				<Container className="outer opening" key={key}>
-					<h4>{permutation._description}</h4>
-					<Card fluid>
-						<Card.Content>
-							<Container className="main">
-								<OpeningBodyStakeRequirement {...permutation} />
-							</Container>
-						</Card.Content>
-					</Card>
-				</Container>
-			))}
-		</Container>
-	)
+  return (
+    <Container>
+      {permutations.map((permutation, key) => (
+        <Container className="outer opening" key={key}>
+          <h4>{permutation._description}</h4>
+          <Card fluid>
+            <Card.Content>
+              <Container className="main">
+                <OpeningBodyStakeRequirement {...permutation} />
+              </Container>
+            </Card.Content>
+          </Card>
+        </Container>
+      ))}
+    </Container>
+  )
 }
 
 export function ReviewInProgress() {
-	const permutations:(OpeningStageClassification & TestProps)[] = [
-		{
-			_description: "Standard control",
-			review_end_time: tomorrow(),
-			review_end_block: 1000000,
-		},
-	]
+  const permutations: (OpeningStageClassification & TestProps)[] = [
+    {
+      _description: "Standard control",
+      review_end_time: tomorrow(),
+      review_end_block: 1000000,
+    },
+  ]
 
-	return (
-		<Container>
-			{permutations.map((permutation, key) => (
-				<Container className="outer opening" key={key}>
-					<h4>{permutation._description}</h4>
-					<Card fluid>
-						<Card.Content>
-							<Container className="main">
-								<OpeningBodyReviewInProgress {...permutation} />
-							</Container>
-						</Card.Content>
-					</Card>
-				</Container>
-			))}
-		</Container>
-	)
+  return (
+    <Container>
+      {permutations.map((permutation, key) => (
+        <Container className="outer opening" key={key}>
+          <h4>{permutation._description}</h4>
+          <Card fluid>
+            <Card.Content>
+              <Container className="main">
+                <OpeningBodyReviewInProgress {...permutation} />
+              </Container>
+            </Card.Content>
+          </Card>
+        </Container>
+      ))}
+    </Container>
+  )
 }
 

+ 112 - 110
packages/joy-roles/src/tabs/Opportunities.stories.tsx

@@ -6,145 +6,147 @@ import { Text, u128 } from '@polkadot/types'
 
 import { Actor } from "@joystream/types/roles"
 import { Opening } from "@joystream/types/hiring"
-import { OpeningView, OpeningBodyApplicationsStatusProps, 
-		 ApplicationStakeRequirement, RoleStakeRequirement,
-		 stateMarkup } from "./Opportunities"
+import {
+  OpeningView, OpeningBodyApplicationsStatusProps,
+  ApplicationStakeRequirement, RoleStakeRequirement,
+  stateMarkup
+} from "./Opportunities"
 
 import { OpeningStageClassification, OpeningState } from "../classifiers"
 
 import 'semantic-ui-css/semantic.min.css'
 import '@polkadot/joy-roles/index.sass'
 
-export default { 
-    title: 'Roles / Components / Opportunities groups tab',
-    decorators: [withKnobs],
-    excludeStories: ['tomorrow', 'yesterday', 'opening', 'creator', 'stateOptions'],
+export default {
+  title: 'Roles / Components / Opportunities groups tab',
+  decorators: [withKnobs],
+  excludeStories: ['tomorrow', 'yesterday', 'opening', 'creator', 'stateOptions'],
 }
 
 export function tomorrow(): Date {
-    const d = new Date()
-    d.setDate(d.getDate() + 1)
-    return d
+  const d = new Date()
+  d.setDate(d.getDate() + 1)
+  return d
 }
 
 export function yesterday(): Date {
-    const d = new Date()
-    d.setDate(d.getDate() - 1)
-    return d
+  const d = new Date()
+  d.setDate(d.getDate() - 1)
+  return d
 }
 
 function newMockHumanReadableText(obj: any) {
-    return new Text(JSON.stringify(obj))
+  return new Text(JSON.stringify(obj))
 }
 
 export const opening = new Opening({
-	max_review_period_length: 50000,
-    human_readable_text: newMockHumanReadableText({
-        version: 1,
-        headline: text("Headline", "Help us curate awesome content", "Role"),
-        job: {
-            title: text("Job title", "Content curator", "Role"),
-            description: text("Job description", faker.lorem.paragraphs(4), "Role")
-        },
-        application: {
-          sections: [
-            {
-              title: "About you",
-              questions: [
-                {
-                  title: "your name",
-                  type: "text"
-                }
-              ]
-            },
+  max_review_period_length: 50000,
+  human_readable_text: newMockHumanReadableText({
+    version: 1,
+    headline: text("Headline", "Help us curate awesome content", "Role"),
+    job: {
+      title: text("Job title", "Content curator", "Role"),
+      description: text("Job description", faker.lorem.paragraphs(4), "Role")
+    },
+    application: {
+      sections: [
+        {
+          title: "About you",
+          questions: [
             {
-              title: "About you",
-              questions: [
-                {
-                  title: "your name",
-                  type: "text area"
-                }
-              ]
+              title: "your name",
+              type: "text"
             }
           ]
         },
-        reward: text("Reward", "10 JOY per block", "Role"),
-        creator: {
-            membership: {
-                handle: text("Creator handle", "ben", "Role")
+        {
+          title: "About you",
+          questions: [
+            {
+              title: "your name",
+              type: "text area"
             }
-        },
-        process: {
-            details: [
-                "Some custom detail"
-            ]
+          ]
         }
-    }),
+      ]
+    },
+    reward: text("Reward", "10 JOY per block", "Role"),
+    creator: {
+      membership: {
+        handle: text("Creator handle", "ben", "Role")
+      }
+    },
+    process: {
+      details: [
+        "Some custom detail"
+      ]
+    }
+  }),
 })
 
 export const creator = {
-    actor: new Actor({member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'}),
-    profile: { 
-        handle: new Text(text("Handle","benholdencrowther", "Creator")),
-    },
-    title: text('Title', 'Group lead', "Creator"),
-    lead: boolean('Lead member', true, "Creator"),
-    stake: new u128(number('Stake', 10, {}, "Creator")),
+  actor: new Actor({ member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp' }),
+  profile: {
+    handle: new Text(text("Handle", "benholdencrowther", "Creator")),
+  },
+  title: text('Title', 'Group lead', "Creator"),
+  lead: boolean('Lead member', true, "Creator"),
+  stake: new u128(number('Stake', 10, {}, "Creator")),
 }
 
-const stateOptions:any = function() {
-    const options:any = {}
-    stateMarkup.forEach( (value, key) => {
-        options[value.description] = key
-    })
-    return options
+const stateOptions: any = function() {
+  const options: any = {}
+  stateMarkup.forEach((value, key) => {
+    options[value.description] = key
+  })
+  return options
 }()
 
-export function OpportunitySandbox(){
-    const stage:OpeningStageClassification = {
-        uri: text("URL (to copy)", "https://some.url/#1", "Opening"),
-        state: select("State", stateOptions, OpeningState.AcceptingApplications, "Opening"),
-        starting_block: number("Created block", 2956498, {}, "Opening"),
-        starting_block_hash: "somehash",
-        created_time: yesterday(),
-		review_end_block: 3956498,
-		review_end_time: tomorrow(),
-    }
-
-    const applicationSliderOptions = {
-        range: true,
-        min: 0,
-        max: 20,
-        step: 1,
-    }
-
-    const moneySliderOptions = {
-        range: true,
-        min: 0,
-        max: 1000000,
-        step: 500,
-    }
-
-    const applications:OpeningBodyApplicationsStatusProps ={
-        numberOfApplications: number("Applications count", 0, applicationSliderOptions, "Role rationing policy"),
-        maxNumberOfApplications: number("Application max", 0, applicationSliderOptions, "Role rationing policy"),
-        requiredApplicationStake: new ApplicationStakeRequirement(
-                                new u128(number("Application stake", 500, moneySliderOptions, "Role stakes")), 
-                           ),
-        requiredRoleStake: new RoleStakeRequirement(
-                                new u128(number("Role stake", 0, moneySliderOptions, "Role stakes")), 
-                           ),
-    }
-
-	const defactoMinimumStake:Balance = new u128(number("Dynamic min stake", 0, moneySliderOptions, "Role stakes"))
-
-    return (
-		<OpeningView opening={opening} 
-			creator={creator} 
-			stage={stage} 
-			applications={applications} 
-			defactoMinimumStake={defactoMinimumStake} 
-			block_time_in_seconds={3}
-		/>
-    )
+export function OpportunitySandbox() {
+  const stage: OpeningStageClassification = {
+    uri: text("URL (to copy)", "https://some.url/#1", "Opening"),
+    state: select("State", stateOptions, OpeningState.AcceptingApplications, "Opening"),
+    starting_block: number("Created block", 2956498, {}, "Opening"),
+    starting_block_hash: "somehash",
+    created_time: yesterday(),
+    review_end_block: 3956498,
+    review_end_time: tomorrow(),
+  }
+
+  const applicationSliderOptions = {
+    range: true,
+    min: 0,
+    max: 20,
+    step: 1,
+  }
+
+  const moneySliderOptions = {
+    range: true,
+    min: 0,
+    max: 1000000,
+    step: 500,
+  }
+
+  const applications: OpeningBodyApplicationsStatusProps = {
+    numberOfApplications: number("Applications count", 0, applicationSliderOptions, "Role rationing policy"),
+    maxNumberOfApplications: number("Application max", 0, applicationSliderOptions, "Role rationing policy"),
+    requiredApplicationStake: new ApplicationStakeRequirement(
+      new u128(number("Application stake", 500, moneySliderOptions, "Role stakes")),
+    ),
+    requiredRoleStake: new RoleStakeRequirement(
+      new u128(number("Role stake", 0, moneySliderOptions, "Role stakes")),
+    ),
+  }
+
+  const defactoMinimumStake: Balance = new u128(number("Dynamic min stake", 0, moneySliderOptions, "Role stakes"))
+
+  return (
+    <OpeningView opening={opening}
+      creator={creator}
+      stage={stage}
+      applications={applications}
+      defactoMinimumStake={defactoMinimumStake}
+      block_time_in_seconds={3}
+    />
+  )
 }   

+ 404 - 405
packages/joy-roles/src/tabs/Opportunities.tsx

@@ -17,536 +17,535 @@ import { Opening } from "@joystream/types/hiring"
 import { OpeningStageClassification, OpeningState } from "../classifiers"
 
 export type headerMarkup = {
-    class: string
-    description: string
-    icon: SemanticICONS
-    iconSpin?: boolean
+  class: string
+  description: string
+  icon: SemanticICONS
+  iconSpin?: boolean
 }
 
 export const stateMarkup = new Map<OpeningState, headerMarkup>([
-    [OpeningState.WaitingToBegin, { 
-        class: "waiting-to-begin", 
-        description: "Waiting to begin",
-        icon: "spinner", 
-        iconSpin: true,
-    }],
-    [OpeningState.AcceptingApplications, { 
-        class: "active", 
-        description: "Accepting applications",
-        icon: "heart",
-    }],
-    [OpeningState.InReview, { 
-        class: "in-review", 
-        description: "Applications in review",
-        icon: "hourglass half",
-    }],
-    [OpeningState.Complete, { 
-        class: "complete", 
-        description: "Hiring complete",
-        icon: "thumbs up",
-    }],
-    [OpeningState.Cancelled, { 
-        class: "cancelled", 
-        description: "Cancelled",
-        icon: "ban",
-    }],
+  [OpeningState.WaitingToBegin, {
+    class: "waiting-to-begin",
+    description: "Waiting to begin",
+    icon: "spinner",
+    iconSpin: true,
+  }],
+  [OpeningState.AcceptingApplications, {
+    class: "active",
+    description: "Accepting applications",
+    icon: "heart",
+  }],
+  [OpeningState.InReview, {
+    class: "in-review",
+    description: "Applications in review",
+    icon: "hourglass half",
+  }],
+  [OpeningState.Complete, {
+    class: "complete",
+    description: "Hiring complete",
+    icon: "thumbs up",
+  }],
+  [OpeningState.Cancelled, {
+    class: "cancelled",
+    description: "Cancelled",
+    icon: "ban",
+  }],
 ])
 
-function openingStateMarkup<T>( state: OpeningState, key: string ): T {
-    const markup = stateMarkup.get(state)
+function openingStateMarkup<T>(state: OpeningState, key: string): T {
+  const markup = stateMarkup.get(state)
 
-    if (typeof markup === "undefined") {
-        return null as unknown as T
-    }
-    
-    return (markup as any)[key]
+  if (typeof markup === "undefined") {
+    return null as unknown as T
+  }
+
+  return (markup as any)[key]
 }
 
-export function openingClass( state: OpeningState ): string {
-    return "status-" + openingStateMarkup<string>(state, "class")
+export function openingClass(state: OpeningState): string {
+  return "status-" + openingStateMarkup<string>(state, "class")
 }
 
-export function openingDescription( state: OpeningState ): string {
-    return openingStateMarkup<string>(state, "description")
+export function openingDescription(state: OpeningState): string {
+  return openingStateMarkup<string>(state, "description")
 }
 
-function openingIcon( state: OpeningState ) {
-    const icon = openingStateMarkup<SemanticICONS>(state, "icon")
-    const spin = openingStateMarkup<boolean>(state, "iconSpin")
+function openingIcon(state: OpeningState) {
+  const icon = openingStateMarkup<SemanticICONS>(state, "icon")
+  const spin = openingStateMarkup<boolean>(state, "iconSpin")
 
-    return <Icon name={icon} loading={spin} />
+  return <Icon name={icon} loading={spin} />
 }
 
 type OpeningHeaderProps = {
-    stage: OpeningStageClassification
+  stage: OpeningStageClassification
 }
 
 export function OpeningHeader(props: OpeningHeaderProps) {
-
-    const onCopy = () => {
-        return false
-    }
-
-    return (
-        <Grid columns="equal">
-            <Grid.Column className="status">
-                <Label ribbon size="large">
-                    {openingIcon(props.stage.state)}
-                    {openingDescription(props.stage.state)}
-                </Label>
-            </Grid.Column>
-            <Grid.Column className="meta" textAlign="right">
-                <Label>
-                    <Icon name="history" /> Created&nbsp;
-                        <Moment unix format="DD/MM/YYYY, HH:MM:SS">{props.stage.created_time.getTime()/1000}</Moment>
-                    <Label.Detail>
-                        <Link to={`#/explorer/query/${props.stage.starting_block_hash}`}>
-                            <Icon name="cube" /> 
-                                <NumberFormat value={props.stage.starting_block}
-                                              displayType="text" 
-                                              thousandSeparator={true} 
-                                />
-                        </Link>
-                    </Label.Detail>
-                </Label>
-                <a>
-                    <CopyToClipboard text={props.stage.uri}>
-                        <Label>
-                            <Icon name="copy" /> Copy link
+  const onCopy = () => {
+    return false
+  }
+
+  return (
+    <Grid columns="equal">
+      <Grid.Column className="status">
+        <Label ribbon size="large">
+          {openingIcon(props.stage.state)}
+          {openingDescription(props.stage.state)}
+        </Label>
+      </Grid.Column>
+      <Grid.Column className="meta" textAlign="right">
+        <Label>
+          <Icon name="history" /> Created&nbsp;
+                        <Moment unix format="DD/MM/YYYY, HH:MM:SS">{props.stage.created_time.getTime() / 1000}</Moment>
+          <Label.Detail>
+            <Link to={`#/explorer/query/${props.stage.starting_block_hash}`}>
+              <Icon name="cube" />
+              <NumberFormat value={props.stage.starting_block}
+                displayType="text"
+                thousandSeparator={true}
+              />
+            </Link>
+          </Label.Detail>
+        </Label>
+        <a>
+          <CopyToClipboard text={props.stage.uri}>
+            <Label>
+              <Icon name="copy" /> Copy link
                         </Label>
-                    </CopyToClipboard>
-                </a>
-            </Grid.Column>
-        </Grid>
-    )
+          </CopyToClipboard>
+        </a>
+      </Grid.Column>
+    </Grid>
+  )
 }
 
 type DynamicMinimumProps = {
-    defactoMinimumStake: Balance
+  defactoMinimumStake: Balance
 }
 
 type OpeningBodyCTAProps = OpeningBodyApplicationsStatusProps & OpeningHeaderProps & OpeningBodyProps
 
 function OpeningBodyCTAView(props: OpeningBodyCTAProps) {
-    if (props.stage.state != OpeningState.AcceptingApplications || applicationImpossible(props.applications)) {
-        return null
-    }
+  if (props.stage.state != OpeningState.AcceptingApplications || applicationImpossible(props.applications)) {
+    return null
+  }
 
-    let message = (
-        <Message positive>
-            <Icon name="check circle" /> No stake required
+  let message = (
+    <Message positive>
+      <Icon name="check circle" /> No stake required
         </Message>
-    )
-
-    if (hasAnyStake(props)) {
-        const balance = !props.defactoMinimumStake.isZero() ? props.defactoMinimumStake : props.requiredApplicationStake.hard.add(props.requiredRoleStake.hard)
-        const plural = (props.requiredApplicationStake.anyRequirement() && props.requiredRoleStake.anyRequirement()) ? "s totalling" : " of"
-        message = (
-            <Message warning>
-                <Icon name="warning sign" /> Stake{plural} at least <strong>{formatBalance(balance)}</strong> required!
+  )
+
+  if (hasAnyStake(props)) {
+    const balance = !props.defactoMinimumStake.isZero() ? props.defactoMinimumStake : props.requiredApplicationStake.hard.add(props.requiredRoleStake.hard)
+    const plural = (props.requiredApplicationStake.anyRequirement() && props.requiredRoleStake.anyRequirement()) ? "s totalling" : " of"
+    message = (
+      <Message warning>
+        <Icon name="warning sign" /> Stake{plural} at least <strong>{formatBalance(balance)}</strong> required!
             </Message>
-            )
-        }
-
-    return (
-        <Container>
-            <Button icon fluid positive size="huge">
-                APPLY NOW
-                <Icon name="angle right" /> 
-            </Button>
-            {message}
-        </Container>
     )
+  }
+
+  return (
+    <Container>
+      <Button icon fluid positive size="huge">
+        APPLY NOW
+                <Icon name="angle right" />
+      </Button>
+      {message}
+    </Container>
+  )
 }
 
 export enum StakeType {
-    Fixed = 0,
-    AtLeast,
+  Fixed = 0,
+  AtLeast,
 }
 
 export interface IStakeRequirement {
-    anyRequirement(): boolean 
-    qualifier(): string | null 
-	value:Balance 
-	fixed(): boolean 
-	atLeast(): boolean 
-    describe(string) 
+  anyRequirement(): boolean
+  qualifier(): string | null
+  value: Balance
+  fixed(): boolean
+  atLeast(): boolean
+  describe(string)
 }
 
 abstract class StakeRequirement {
-    hard: Balance
-    type: StakeType
+  hard: Balance
+  type: StakeType
 
-    constructor(hard: Balance, stakeType: StakeType = StakeType.Fixed) {
-        this.hard = hard
-        this.type = stakeType
-    }
+  constructor(hard: Balance, stakeType: StakeType = StakeType.Fixed) {
+    this.hard = hard
+    this.type = stakeType
+  }
 
-    anyRequirement(): boolean {
-        return !this.hard.isZero()
-    }
+  anyRequirement(): boolean {
+    return !this.hard.isZero()
+  }
 
-    qualifier(): string | null {
-        if (this.type == StakeType.AtLeast) {
-            return "at least"
-        }
-        return null
+  qualifier(): string | null {
+    if (this.type == StakeType.AtLeast) {
+      return "at least"
     }
+    return null
+  }
 
-	get value():Balance {
-		return this.hard
-	}
+  get value(): Balance {
+    return this.hard
+  }
 
-	fixed(): boolean {
-		return this.type === StakeType.Fixed
-	}
+  fixed(): boolean {
+    return this.type === StakeType.Fixed
+  }
 
-	atLeast(): boolean {
-		return this.type === StakeType.AtLeast
-	}
+  atLeast(): boolean {
+    return this.type === StakeType.AtLeast
+  }
 }
 
 export class ApplicationStakeRequirement extends StakeRequirement implements IStakeRequirement {
-    describe(name:string) {
-        if (!this.anyRequirement()) {
-            return null
-        }
-
-        return (
-            <p>
-                You must stake {this.qualifier()} <strong>{formatBalance(this.hard)}</strong> to apply for this role. This stake will be returned to you when the hiring process is complete, whether or not you are hired, and will also be used to rank applications.
-            </p>
-        )
+  describe(name: string) {
+    if (!this.anyRequirement()) {
+      return null
     }
+
+    return (
+      <p>
+        You must stake {this.qualifier()} <strong>{formatBalance(this.hard)}</strong> to apply for this role. This stake will be returned to you when the hiring process is complete, whether or not you are hired, and will also be used to rank applications.
+            </p>
+    )
+  }
 }
 
 export class RoleStakeRequirement extends StakeRequirement implements IStakeRequirement {
-    describe(name:string) {
-        if (!this.anyRequirement()) {
-            return null
-        }
-
-        return (
-            <p>
-                 You must stake {this.qualifier()} <strong>{formatBalance(this.hard)}</strong> to be eligible for this role. You may lose this stake if you're hired and then dismised from this role. This stake will be returned if your application is unsuccessful, and will also be used to rank applications. 
-            </p>
-        )
+  describe(name: string) {
+    if (!this.anyRequirement()) {
+      return null
     }
+
+    return (
+      <p>
+        You must stake {this.qualifier()} <strong>{formatBalance(this.hard)}</strong> to be eligible for this role. You may lose this stake if you're hired and then dismised from this role. This stake will be returned if your application is unsuccessful, and will also be used to rank applications.
+            </p>
+    )
+  }
 }
 
 export type StakeRequirementProps = DynamicMinimumProps & {
-    requiredApplicationStake: ApplicationStakeRequirement
-    requiredRoleStake: RoleStakeRequirement
-    maxNumberOfApplications: number
+  requiredApplicationStake: ApplicationStakeRequirement
+  requiredRoleStake: RoleStakeRequirement
+  maxNumberOfApplications: number
 }
 
 function hasAnyStake(props: StakeRequirementProps): boolean {
-    return props.requiredApplicationStake.anyRequirement() || props.requiredRoleStake.anyRequirement()
+  return props.requiredApplicationStake.anyRequirement() || props.requiredRoleStake.anyRequirement()
 }
 
 class messageState {
-    positive: boolean = true
-    warning: boolean = false
-    negative: boolean = false
-
-    setPositive():void {
-        this.positive = true
-        this.warning = false
-        this.negative = false
-    }
+  positive: boolean = true
+  warning: boolean = false
+  negative: boolean = false
 
-    setWarning():void {
-        this.positive = false
-        this.warning = true
-        this.negative = false
-    }
+  setPositive(): void {
+    this.positive = true
+    this.warning = false
+    this.negative = false
+  }
 
-    setNegative():void {
-        this.positive = false
-        this.warning = false
-        this.negative = true
-    }
+  setWarning(): void {
+    this.positive = false
+    this.warning = true
+    this.negative = false
+  }
+
+  setNegative(): void {
+    this.positive = false
+    this.warning = false
+    this.negative = true
+  }
 }
 
 export function OpeningBodyStakeRequirement(props: StakeRequirementProps) {
-    if (!hasAnyStake(props)) {
-        return null
-    }
-
-    const plural = (props.requiredApplicationStake.anyRequirement() && props.requiredRoleStake.anyRequirement()) ? "s" : null
-    let title = <Message.Header color="orange" as='h5'><Icon name="shield" /> Stake{plural} required!</Message.Header>
-    let explanation = null
-
-    if (!props.defactoMinimumStake.isZero()) {
-        title = <Message.Header color="orange" as='h5'><Icon name="shield" /> Increased stake{plural} required!</Message.Header>
-        explanation = (
-            <p>
-                However, in order to be in the top {props.maxNumberOfApplications} candidates, you wil need to stake at least <strong>{formatBalance(props.defactoMinimumStake)} in total</strong>.
+  if (!hasAnyStake(props)) {
+    return null
+  }
+
+  const plural = (props.requiredApplicationStake.anyRequirement() && props.requiredRoleStake.anyRequirement()) ? "s" : null
+  let title = <Message.Header color="orange" as='h5'><Icon name="shield" /> Stake{plural} required!</Message.Header>
+  let explanation = null
+
+  if (!props.defactoMinimumStake.isZero()) {
+    title = <Message.Header color="orange" as='h5'><Icon name="shield" /> Increased stake{plural} required!</Message.Header>
+    explanation = (
+      <p>
+        However, in order to be in the top {props.maxNumberOfApplications} candidates, you wil need to stake at least <strong>{formatBalance(props.defactoMinimumStake)} in total</strong>.
             </p>
-        )
-    }
-
-    return (
-        <Message className="stake-requirements" warning>
-            {title}
-            {props.requiredApplicationStake.describe()}
-            {props.requiredRoleStake.describe()}
-            {explanation}
-        </Message>
     )
+  }
+
+  return (
+    <Message className="stake-requirements" warning>
+      {title}
+      {props.requiredApplicationStake.describe()}
+      {props.requiredRoleStake.describe()}
+      {explanation}
+    </Message>
+  )
 }
 
 export type ApplicationCountProps = {
-    numberOfApplications: number
-    maxNumberOfApplications: number
-    applied?: boolean
+  numberOfApplications: number
+  maxNumberOfApplications: number
+  applied?: boolean
 }
 
 export type OpeningBodyApplicationsStatusProps = StakeRequirementProps & ApplicationCountProps
 
 function applicationImpossible(props: OpeningBodyApplicationsStatusProps): boolean {
-    return props.maxNumberOfApplications > 0 && 
-           (props.numberOfApplications >= props.maxNumberOfApplications) && 
-           !hasAnyStake(props)
+  return props.maxNumberOfApplications > 0 &&
+    (props.numberOfApplications >= props.maxNumberOfApplications) &&
+    !hasAnyStake(props)
 }
 
 function applicationPossibleWithIncresedStake(props: OpeningBodyApplicationsStatusProps): boolean {
-    return props.maxNumberOfApplications > 0 && 
-           (props.numberOfApplications >= props.maxNumberOfApplications) && 
-           hasAnyStake(props)
+  return props.maxNumberOfApplications > 0 &&
+    (props.numberOfApplications >= props.maxNumberOfApplications) &&
+    hasAnyStake(props)
 }
 
-export function ApplicationCount(props: ApplicationCountProps) { 
-    let max_applications = null
-    if (props.maxNumberOfApplications > 0) {
-        max_applications = (
-            <span>
-                /
+export function ApplicationCount(props: ApplicationCountProps) {
+  let max_applications = null
+  if (props.maxNumberOfApplications > 0) {
+    max_applications = (
+      <span>
+        /
                 <NumberFormat value={props.maxNumberOfApplications}
-                              displayType="text" 
-                              thousandSeparator={true} 
-                />
-            </span>
-        )
-    }
-
-    return (
-        <span>
-            <NumberFormat value={props.numberOfApplications + (props.applied ? 1 : 0)}
-                          displayType="text" 
-                          thousandSeparator={true} 
-            />
-            {max_applications}
-        </span>
+          displayType="text"
+          thousandSeparator={true}
+        />
+      </span>
     )
+  }
+
+  return (
+    <span>
+      <NumberFormat value={props.numberOfApplications + (props.applied ? 1 : 0)}
+        displayType="text"
+        thousandSeparator={true}
+      />
+      {max_applications}
+    </span>
+  )
 }
 
 export function OpeningBodyApplicationsStatus(props: OpeningBodyApplicationsStatusProps) {
-    const impossible = applicationImpossible(props)
-    const msg = new messageState()
-    if (impossible) {
-        msg.setNegative()
-    } else if (applicationPossibleWithIncresedStake(props)) {
-        msg.setWarning()
-    }
-
-    let order = null
-    if (hasAnyStake(props)) {
-        order = ", ordered by total stake,"
-    }
-
-    let max_applications = null
-    let message = <p>All applications{order} will be considered during the review period.</p>
-
-    if (props.maxNumberOfApplications > 0) {
-        max_applications = (
-            <span>
-                /
+  const impossible = applicationImpossible(props)
+  const msg = new messageState()
+  if (impossible) {
+    msg.setNegative()
+  } else if (applicationPossibleWithIncresedStake(props)) {
+    msg.setWarning()
+  }
+
+  let order = null
+  if (hasAnyStake(props)) {
+    order = ", ordered by total stake,"
+  }
+
+  let max_applications = null
+  let message = <p>All applications{order} will be considered during the review period.</p>
+
+  if (props.maxNumberOfApplications > 0) {
+    max_applications = (
+      <span>
+        /
                 <NumberFormat value={props.maxNumberOfApplications}
-                              displayType="text" 
-                              thousandSeparator={true} 
-                />
-            </span>
-        )
-
-        let disclaimer = null
-        if (impossible) {
-            disclaimer = "No futher applications will be considered."
-        }
+          displayType="text"
+          thousandSeparator={true}
+        />
+      </span>
+    )
 
-        message = (
-            <p>Only the top {props.maxNumberOfApplications} applications{order} will be considered for this role. {disclaimer}</p>
-        )
+    let disclaimer = null
+    if (impossible) {
+      disclaimer = "No futher applications will be considered."
     }
 
-    return (
-        <Message positive={msg.positive} warning={msg.warning} negative={msg.negative}>
-            <Statistic size="small">
-                <Statistic.Value>
-                    <NumberFormat value={props.numberOfApplications + (props.applied ? 1 : 0)}
-                                  displayType="text" 
-                                  thousandSeparator={true} 
-                    />
-                    {max_applications}
-                </Statistic.Value> 
-                <Statistic.Label>Applications</Statistic.Label>
-            </Statistic>
-            {message}
-        </Message>
+    message = (
+      <p>Only the top {props.maxNumberOfApplications} applications{order} will be considered for this role. {disclaimer}</p>
     )
+  }
+
+  return (
+    <Message positive={msg.positive} warning={msg.warning} negative={msg.negative}>
+      <Statistic size="small">
+        <Statistic.Value>
+          <NumberFormat value={props.numberOfApplications + (props.applied ? 1 : 0)}
+            displayType="text"
+            thousandSeparator={true}
+          />
+          {max_applications}
+        </Statistic.Value>
+        <Statistic.Label>Applications</Statistic.Label>
+      </Statistic>
+      {message}
+    </Message>
+  )
 }
 
 export function OpeningBodyReviewInProgress(props: OpeningStageClassification) {
-    let countdown = null
-    if (typeof props.review_end_time !== "undefined") {
-        countdown = <Countdown end={props.review_end_time} />
-    }
-
-    return (
-        <Message info className="countdown">
-            <h4>Review process has begun</h4>
-            {countdown}
-
-            <p>
-				<span>Candidates will be selected by block&nbsp;
+  let countdown = null
+  if (typeof props.review_end_time !== "undefined") {
+    countdown = <Countdown end={props.review_end_time} />
+  }
+
+  return (
+    <Message info className="countdown">
+      <h4>Review process has begun</h4>
+      {countdown}
+
+      <p>
+        <span>Candidates will be selected by block&nbsp;
 					<NumberFormat value={props.review_end_block}
-						  displayType="text" 
-						  thousandSeparator={true} 
-					/>
-					&nbsp;(expected on&nbsp;
+            displayType="text"
+            thousandSeparator={true}
+          />
+          &nbsp;(expected on&nbsp;
                 </span>
-                <strong>
-                    <Moment format="MMM DD, YYYY  HH:mm:ss" date={props.review_end_time} interval={0}/>
-                </strong>
-				)
+        <strong>
+          <Moment format="MMM DD, YYYY  HH:mm:ss" date={props.review_end_time} interval={0} />
+        </strong>
+        )
                 <span> at the latest.</span>
-            </p>
-        </Message>
-    )
+      </p>
+    </Message>
+  )
 }
 
 type BlockTimeProps = {
-    block_time_in_seconds: number
+  block_time_in_seconds: number
 }
 
 function timeInHumanFormat(block_time_in_seconds: number, blocks: number) {
-    const d1 = new Date()
-    const d2 = new Date(d1.getTime())
-    d2.setSeconds(d2.getSeconds() + (block_time_in_seconds * blocks))
-    return <Moment duration={d1} date={d2} interval={0}/>
+  const d1 = new Date()
+  const d2 = new Date(d1.getTime())
+  d2.setSeconds(d2.getSeconds() + (block_time_in_seconds * blocks))
+  return <Moment duration={d1} date={d2} interval={0} />
 }
 
 
 export type OpeningBodyProps = DynamicMinimumProps & StakeRequirementProps & BlockTimeProps & {
-    opening: Opening
-    text: GenericJoyStreamRoleSchema
-    creator: GroupMemberProps
-    stage: OpeningStageClassification
-    applications: OpeningBodyApplicationsStatusProps
+  opening: Opening
+  text: GenericJoyStreamRoleSchema
+  creator: GroupMemberProps
+  stage: OpeningStageClassification
+  applications: OpeningBodyApplicationsStatusProps
 }
 
 export function OpeningBody(props: OpeningBodyProps) {
-    const jobDesc = marked(props.text.job.description || '')
-    const blockNumber = <NumberFormat value={props.opening.max_review_period_length.toNumber()}
-                                      displayType="text" 
-                                      thousandSeparator={true} />
-
-    let stakeRequirements = null
-    switch (props.stage.state) {
-        case OpeningState.WaitingToBegin:
-        case OpeningState.AcceptingApplications:
-            stakeRequirements = <OpeningBodyStakeRequirement {...props.applications} defactoMinimumStake={props.defactoMinimumStake} />
-            break
-
-        case OpeningState.InReview:
-            stakeRequirements = <OpeningBodyReviewInProgress {...props.stage} />
-                break
-    }
-
-    return (
-        <Grid columns="equal">
-            <Grid.Column width={10} className="summary">
-                <Card.Header>
-                    <OpeningReward text={props.text.reward} />
-                </Card.Header>
-                <h4 className="headline">{props.text.headline}</h4>
-                <h5>Role description</h5>
-                <div dangerouslySetInnerHTML={{__html: jobDesc}} /> 
-                <h5>Hiring process details</h5>
-                <List>
-                    <List.Item>
-                        <List.Icon name="clock" />
-                            <List.Content>
-                                The maximum review period for this opening is <strong>{blockNumber} blocks</strong> (approximately <strong>{timeInHumanFormat(props.block_time_in_seconds, props.opening.max_review_period_length.toNumber())}</strong>).
+  const jobDesc = marked(props.text.job.description || '')
+  const blockNumber = <NumberFormat value={props.opening.max_review_period_length.toNumber()}
+    displayType="text"
+    thousandSeparator={true} />
+
+  let stakeRequirements = null
+  switch (props.stage.state) {
+    case OpeningState.WaitingToBegin:
+    case OpeningState.AcceptingApplications:
+      stakeRequirements = <OpeningBodyStakeRequirement {...props.applications} defactoMinimumStake={props.defactoMinimumStake} />
+      break
+
+    case OpeningState.InReview:
+      stakeRequirements = <OpeningBodyReviewInProgress {...props.stage} />
+      break
+  }
+
+  return (
+    <Grid columns="equal">
+      <Grid.Column width={10} className="summary">
+        <Card.Header>
+          <OpeningReward text={props.text.reward} />
+        </Card.Header>
+        <h4 className="headline">{props.text.headline}</h4>
+        <h5>Role description</h5>
+        <div dangerouslySetInnerHTML={{ __html: jobDesc }} />
+        <h5>Hiring process details</h5>
+        <List>
+          <List.Item>
+            <List.Icon name="clock" />
+            <List.Content>
+              The maximum review period for this opening is <strong>{blockNumber} blocks</strong> (approximately <strong>{timeInHumanFormat(props.block_time_in_seconds, props.opening.max_review_period_length.toNumber())}</strong>).
                         </List.Content>
-                    </List.Item>
-                    {props.text.process.details.map((detail,key) => (
-                        <List.Item key={key}>
-                            <List.Icon name="info circle" />
-                            <List.Content>{detail}</List.Content>
-                        </List.Item>
-                    ))}
-                </List>
-            </Grid.Column>
-            <Grid.Column width={6} className="details">
-                <OpeningBodyApplicationsStatus {...props.applications} />
-                {stakeRequirements}
-                <h5>Group lead</h5>
-                <GroupMemberView {...props.creator} inset={true} />
-                <OpeningBodyCTAView {...props} {...props.applications} />
-            </Grid.Column>
-        </Grid>
-    )
+          </List.Item>
+          {props.text.process.details.map((detail, key) => (
+            <List.Item key={key}>
+              <List.Icon name="info circle" />
+              <List.Content>{detail}</List.Content>
+            </List.Item>
+          ))}
+        </List>
+      </Grid.Column>
+      <Grid.Column width={6} className="details">
+        <OpeningBodyApplicationsStatus {...props.applications} />
+        {stakeRequirements}
+        <h5>Group lead</h5>
+        <GroupMemberView {...props.creator} inset={true} />
+        <OpeningBodyCTAView {...props} {...props.applications} />
+      </Grid.Column>
+    </Grid>
+  )
 }
 
 type OpeningRewardProps = {
-    text: string
+  text: string
 }
 
 function OpeningReward(props: OpeningRewardProps) {
-    return (
-        <Statistic size="small">
-            <Statistic.Label>Reward</Statistic.Label>
-            <Statistic.Value>{props.text}</Statistic.Value> 
-        </Statistic>
-    )
+  return (
+    <Statistic size="small">
+      <Statistic.Label>Reward</Statistic.Label>
+      <Statistic.Value>{props.text}</Statistic.Value>
+    </Statistic>
+  )
 }
 
 type Props = OpeningHeaderProps & DynamicMinimumProps & BlockTimeProps & {
-    opening: Opening
-    creator: GroupMemberProps
-    applications: OpeningBodyApplicationsStatusProps
+  opening: Opening
+  creator: GroupMemberProps
+  applications: OpeningBodyApplicationsStatusProps
 }
 
 export function OpeningView(props: Props) {
-    const hrt = props.opening.human_readable_text
-
-    if (typeof hrt === "undefined") {
-        return null
-    } else if (typeof hrt === "string") {
-        return "FIXME: what do we do?"
-    }
-
-    const text = hrt as GenericJoyStreamRoleSchema
-
-    return (
-        <Container className={"opening "+openingClass(props.stage.state)}>
-            <h3>{text.job.title}</h3>
-            <Card fluid className="container">
-                <Card.Content className="header">
-                    <OpeningHeader stage={props.stage} />
-                </Card.Content>
-                <Card.Content className="main">
-                    <OpeningBody text={text} 
-                                 opening={props.opening}
-                                 creator={props.creator} 
-                                 stage={props.stage} 
-                                 applications={props.applications}
-                                 defactoMinimumStake={props.defactoMinimumStake}
-                                 block_time_in_seconds={props.block_time_in_seconds}
-                    />
-                </Card.Content>
-            </Card>
-        </Container>
-    )
+  const hrt = props.opening.human_readable_text
+
+  if (typeof hrt === "undefined") {
+    return null
+  } else if (typeof hrt === "string") {
+    return "FIXME: what do we do?"
+  }
+
+  const text = hrt as GenericJoyStreamRoleSchema
+
+  return (
+    <Container className={"opening " + openingClass(props.stage.state)}>
+      <h3>{text.job.title}</h3>
+      <Card fluid className="container">
+        <Card.Content className="header">
+          <OpeningHeader stage={props.stage} />
+        </Card.Content>
+        <Card.Content className="main">
+          <OpeningBody text={text}
+            opening={props.opening}
+            creator={props.creator}
+            stage={props.stage}
+            applications={props.applications}
+            defactoMinimumStake={props.defactoMinimumStake}
+            block_time_in_seconds={props.block_time_in_seconds}
+          />
+        </Card.Content>
+      </Card>
+    </Container>
+  )
 }

+ 87 - 85
packages/joy-roles/src/tabs/WorkingGroup.stories.tsx

@@ -13,100 +13,102 @@ import { GroupMemberProps } from "@polkadot/joy-roles/elements"
 import 'semantic-ui-css/semantic.min.css'
 import '@polkadot/joy-roles/index.sass'
 
-export default { 
+export default {
   title: 'Roles / Components / Working groups tab',
-    decorators: [withKnobs],
+  decorators: [withKnobs],
 }
 
-export function ContentCuratorsSection(){
-	const members:GroupMemberProps[] = [
-		{
-			actor: new Actor({member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'}),
-			profile: { 
-				handle: new Text(text("Handle", "benholdencrowther", "Ben")),
-				avatar_uri: new Text(text("Avatar URL", "https://www.benholdencrowther.com/wp-content/uploads/2019/03/Hanging_Gardens_of_Babylon.jpg", "Ben")),
-			},
-			title: text("Title", 'Curation lead', "Ben"),
-			lead: boolean("Lead", true, "Ben"),
-			stake: new u128(number("Stake", 10101, {}, "Ben")),
-			earned: new u128(number("Earned", 347829, {}, "Ben")),
-		},
-		{
-			actor: new Actor({member_id: 2, account: '5DfJWGbBAH8hLAg8rcRYZW5BEZbE4BJeCQKoxUeqoyewLSew'}),
-			profile: { handle: new Text(text("Handle", "bwhm0", "Martin")) },
-			title: text('Title', 'Content curator', 'Martin'),
-			lead: boolean("Lead", false, "Martin"),
-			stake: new u128(number("Stake", 10101, {}, "Martin")),
-			earned: new u128(number("Earned", 347829, {}, "Martin")),
-		},
-		{
-			actor: new Actor({member_id: 3, account: '5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'}),
-			profile: { 
-				handle: new Text("yourheropaul"),
-				avatar_uri: new Text("https://yhp.io/img/paul.svg"),
-			},
-			title: text('Title', 'Content curator', 'Paul'),
-			lead: boolean("Lead", false, "Paul"),
-			stake: new u128(number("Stake", 10101, {}, "Paul")),
-			earned: new u128(number("Earned", 347829, {}, "Paul")),
-		},
-		{
-			actor: new Actor({member_id: 2, account: '5GSMNn8Sy8k64mGUWPDafjMZu9bQNX26GujbBQ1LeJpNbrfg'}),
-			profile: { 
-				handle: new Text("alex_joystream"),
-				avatar_uri: new Text("https://avatars2.githubusercontent.com/u/153928?s=200&v=4"),
-			},
-			title: text('Title', 'Content curator', 'Alex'),
-			lead: boolean("Lead", false, "Alex"),
-			stake: new u128(number("Stake", 10101, {}, "Alex")),
-			earned: new u128(number("Earned", 347829, {}, "Alex")),
-		},
-		{
-			actor: new Actor({member_id: 3, account: '5Gn9n7SDJ7VgHqHQWYzkSA4vX6DCmS5TFWdHxikTXp9b4L32'}),
-			profile: { 
-				handle: new Text("mokhtar"), 
-				avatar_uri: new Text("https://avatars2.githubusercontent.com/u/1621012?s=460&v=4"),
-			},
-			title: text('Title', 'Content curator', 'Mokhtar'),
-			lead: boolean("Lead", false, "Mokhtar"),
-			stake: new u128(number("Stake", 10101, {}, "Mokhtar")),
-			earned: new u128(number("Earned", 347829, {}, "Mokhtar")),
-		},
-	]
+export function ContentCuratorsSection() {
+  const members: GroupMemberProps[] = [
+    {
+      actor: new Actor({ member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp' }),
+      profile: {
+        handle: new Text(text("Handle", "benholdencrowther", "Ben")),
+        avatar_uri: new Text(text("Avatar URL", "https://www.benholdencrowther.com/wp-content/uploads/2019/03/Hanging_Gardens_of_Babylon.jpg", "Ben")),
+      },
+      title: text("Title", 'Curation lead', "Ben"),
+      lead: boolean("Lead", true, "Ben"),
+      stake: new u128(number("Stake", 10101, {}, "Ben")),
+      earned: new u128(number("Earned", 347829, {}, "Ben")),
+    },
+    {
+      actor: new Actor({ member_id: 2, account: '5DfJWGbBAH8hLAg8rcRYZW5BEZbE4BJeCQKoxUeqoyewLSew' }),
+      profile: { handle: new Text(text("Handle", "bwhm0", "Martin")) },
+      title: text('Title', 'Content curator', 'Martin'),
+      lead: boolean("Lead", false, "Martin"),
+      stake: new u128(number("Stake", 10101, {}, "Martin")),
+      earned: new u128(number("Earned", 347829, {}, "Martin")),
+    },
+    {
+      actor: new Actor({ member_id: 3, account: '5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3' }),
+      profile: {
+        handle: new Text("yourheropaul"),
+        avatar_uri: new Text("https://yhp.io/img/paul.svg"),
+      },
+      title: text('Title', 'Content curator', 'Paul'),
+      lead: boolean("Lead", false, "Paul"),
+      stake: new u128(number("Stake", 10101, {}, "Paul")),
+      earned: new u128(number("Earned", 347829, {}, "Paul")),
+    },
+    {
+      actor: new Actor({ member_id: 2, account: '5GSMNn8Sy8k64mGUWPDafjMZu9bQNX26GujbBQ1LeJpNbrfg' }),
+      profile: {
+        handle: new Text("alex_joystream"),
+        avatar_uri: new Text("https://avatars2.githubusercontent.com/u/153928?s=200&v=4"),
+      },
+      title: text('Title', 'Content curator', 'Alex'),
+      lead: boolean("Lead", false, "Alex"),
+      stake: new u128(number("Stake", 10101, {}, "Alex")),
+      earned: new u128(number("Earned", 347829, {}, "Alex")),
+    },
+    {
+      actor: new Actor({ member_id: 3, account: '5Gn9n7SDJ7VgHqHQWYzkSA4vX6DCmS5TFWdHxikTXp9b4L32' }),
+      profile: {
+        handle: new Text("mokhtar"),
+        avatar_uri: new Text("https://avatars2.githubusercontent.com/u/1621012?s=460&v=4"),
+      },
+      title: text('Title', 'Content curator', 'Mokhtar'),
+      lead: boolean("Lead", false, "Mokhtar"),
+      stake: new u128(number("Stake", 10101, {}, "Mokhtar")),
+      earned: new u128(number("Earned", 347829, {}, "Mokhtar")),
+    },
+  ]
 
-	return (
-		<ContentCurators members={members} rolesAvailable={boolean('Roles available', true)}/>
-	)
+  return (
+    <ContentCurators members={members} rolesAvailable={boolean('Roles available', true)} />
+  )
 }
 
 export const StorageProvidersSection = () => {
-	const balances = new Map<string, Balance>([
-		['5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp', new u128(101)],
-	])
+  const balances = new Map<string, Balance>([
+    ['5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp', new u128(101)],
+  ])
 
-	const memos = new Map<string, Text>([
-		['5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp', new Text("hello")]
-	])
+  const memos = new Map<string, Text>([
+    ['5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp', new Text("hello")]
+  ])
 
-	const profiles = new Map<number, Profile>([
-		[1, { handle: new Text("bwhm0") }],
-		[2, { handle: new Text("benholdencrowther"), 
-			  avatar_uri: new Text("https://www.benholdencrowther.com/wp-content/uploads/2019/03/Hanging_Gardens_of_Babylon.jpg") }],
-	])
+  const profiles = new Map<number, Profile>([
+    [1, { handle: new Text("bwhm0") }],
+    [2, {
+      handle: new Text("benholdencrowther"),
+      avatar_uri: new Text("https://www.benholdencrowther.com/wp-content/uploads/2019/03/Hanging_Gardens_of_Babylon.jpg")
+    }],
+  ])
 
-	const storageProviders:Actor[] = [
-		new Actor({member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp'}),
-		new Actor({member_id: 2, account: '5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3'}),
-	];
+  const storageProviders: Actor[] = [
+    new Actor({ member_id: 1, account: '5HZ6GtaeyxagLynPryM7ZnmLzoWFePKuDrkb4AT8rT4pU1fp' }),
+    new Actor({ member_id: 2, account: '5DQqNWRFPruFs9YKheVMqxUbqoXeMzAWfVfcJgzuia7NA3D3' }),
+  ];
 
-	return (
-		<div>
-			<StorageAndDistribution 
-				actors={storageProviders} 
-				balances={balances} 
-				memos={memos} 
-				profiles={profiles} 
-			/>
-		</div>
-	)
+  return (
+    <div>
+      <StorageAndDistribution
+        actors={storageProviders}
+        balances={balances}
+        memos={memos}
+        profiles={profiles}
+      />
+    </div>
+  )
 }

+ 66 - 66
packages/joy-roles/src/tabs/WorkingGroup.tsx

@@ -9,85 +9,85 @@ import { Text } from '@polkadot/types';
 import { ActorDetailsView, GroupMemberProps, MemberView, GroupMemberView } from "../elements"
 
 type WorkingGroupProps = {
-	members: GroupMemberProps[]
-	rolesAvailable: boolean
+  members: GroupMemberProps[]
+  rolesAvailable: boolean
 }
 
 export function ContentCurators(props: WorkingGroupProps) {
-	let message = (
-		<Message>
-			<Message.Header>No open roles at the moment</Message.Header>
-			<p>The team is full at the moment, but we intend to expand. Check back for open roles soon!</p>
-		</Message>
-	)
+  let message = (
+    <Message>
+      <Message.Header>No open roles at the moment</Message.Header>
+      <p>The team is full at the moment, but we intend to expand. Check back for open roles soon!</p>
+    </Message>
+  )
 
-	if (props.rolesAvailable) {
-		message = (
-			<Message positive>
-				<Message.Header>Join us and get paid to curate!</Message.Header>
-				<p>
-					Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+  if (props.rolesAvailable) {
+    message = (
+      <Message positive>
+        <Message.Header>Join us and get paid to curate!</Message.Header>
+        <p>
+          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
 				</p>
-				<Button positive>Find out more &raquo;</Button>
-		  </Message>
-		)
-	}
+        <Button positive>Find out more &raquo;</Button>
+      </Message>
+    )
+  }
 
-    return (
+  return (
     <section id="content-curators">
-		<h2>Content curators</h2>
-		<p>
-			Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+      <h2>Content curators</h2>
+      <p>
+        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
 		</p>
-		<Card.Group>
-			{props.members.map((member, key) => (
-				<GroupMemberView {...member} />
-			))}
-		</Card.Group>
-		{message}
-	</section>
-	)
+      <Card.Group>
+        {props.members.map((member, key) => (
+          <GroupMemberView {...member} />
+        ))}
+      </Card.Group>
+      {message}
+    </section>
+  )
 }
 
 type StorageAndDistributionProps = {
-    actors: Actor[]
-    balances: Map<string, Balance>
-    memos: Map<string, Text>
-    profiles: Map<number, Profile>
+  actors: Actor[]
+  balances: Map<string, Balance>
+  memos: Map<string, Text>
+  profiles: Map<number, Profile>
 }
 
 export function StorageAndDistribution(props: StorageAndDistributionProps) {
-    return (
+  return (
     <section id="storage-providers">
-        <h2>Storage and distribution</h2>
-        <Table basic='very'>
-                <Table.Header>
-                    <Table.Row>
-                        <Table.HeaderCell>Member</Table.HeaderCell>
-                        <Table.HeaderCell>Details</Table.HeaderCell>
-                    </Table.Row>
-                </Table.Header>
-                <Table.Body>
-                    {props.actors.map((actor, key) => (
-                        <Table.Row key={key}>
-							<Table.Cell>
-								<MemberView 
-									actor={actor} 
-									balance={props.balances.get(actor.account.toString())}
-									profile={props.profiles.get(actor.member_id.toNumber())}
-								/>
-							</Table.Cell>
-							<Table.Cell>
-								<ActorDetailsView 
-									actor={actor} 
-									balance={props.balances.get(actor.account.toString())} 
-									memo={props.memos.get(actor.account.toString())} 
-								/>
-							</Table.Cell>
-                        </Table.Row>
-                    ))}
-                </Table.Body>
-            </Table>
-        </section>
-    )
+      <h2>Storage and distribution</h2>
+      <Table basic='very'>
+        <Table.Header>
+          <Table.Row>
+            <Table.HeaderCell>Member</Table.HeaderCell>
+            <Table.HeaderCell>Details</Table.HeaderCell>
+          </Table.Row>
+        </Table.Header>
+        <Table.Body>
+          {props.actors.map((actor, key) => (
+            <Table.Row key={key}>
+              <Table.Cell>
+                <MemberView
+                  actor={actor}
+                  balance={props.balances.get(actor.account.toString())}
+                  profile={props.profiles.get(actor.member_id.toNumber())}
+                />
+              </Table.Cell>
+              <Table.Cell>
+                <ActorDetailsView
+                  actor={actor}
+                  balance={props.balances.get(actor.account.toString())}
+                  memo={props.memos.get(actor.account.toString())}
+                />
+              </Table.Cell>
+            </Table.Row>
+          ))}
+        </Table.Body>
+      </Table>
+    </section>
+  )
 }

+ 24 - 0
tsfmt.json

@@ -0,0 +1,24 @@
+{
+  "baseIndentSize": 0,
+  "indentSize": 4,
+  "tabSize": 4,
+  "indentStyle": 2,
+  "newLineCharacter": "\r\n",
+  "convertTabsToSpaces": true,
+  "insertSpaceAfterCommaDelimiter": true,
+  "insertSpaceAfterSemicolonInForStatements": true,
+  "insertSpaceBeforeAndAfterBinaryOperators": true,
+  "insertSpaceAfterConstructor": false,
+  "insertSpaceAfterKeywordsInControlFlowStatements": true,
+  "insertSpaceAfterFunctionKeywordForAnonymousFunctions": false,
+  "insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
+  "insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": false,
+  "insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": true,
+  "insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
+  "insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces": false,
+  "insertSpaceAfterTypeAssertion": false,
+  "insertSpaceBeforeFunctionParenthesis": false,
+  "insertSpaceBeforeTypeAnnotation": false,
+  "placeOpenBraceOnNewLineForFunctions": false,
+  "placeOpenBraceOnNewLineForControlBlocks": false
+}

+ 29 - 1
yarn.lock

@@ -6269,6 +6269,11 @@ commander@~2.20.3:
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
   integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
 
+commandpost@^1.0.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/commandpost/-/commandpost-1.4.0.tgz#89218012089dfc9b67a337ba162f15c88e0f1048"
+  integrity sha512-aE2Y4MTFJ870NuB/+2z1cXBhSBBzRydVVjzhFC4gtenEhpnj15yu0qptWGJsO9YGrcPZ3ezX8AWb1VA391MKpQ==
+
 common-tags@^1.8.0:
   version "1.8.0"
   resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@@ -7684,6 +7689,16 @@ edgeware-node-types@^1.0.10:
   resolved "https://registry.yarnpkg.com/edgeware-node-types/-/edgeware-node-types-1.0.10.tgz#eb40ef4308f84d64975edc99d12ac5da891ee6a3"
   integrity sha512-iGMjkxtaT0FE5TR8DTucF+63IbtGHiieNxNkPTlsGYGt1JaPc6aP0ApnSQKYu2NiQaVcg1RB+BDMmGfFk1bnhw==
 
+editorconfig@^0.15.0:
+  version "0.15.3"
+  resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
+  integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==
+  dependencies:
+    commander "^2.19.0"
+    lru-cache "^4.1.5"
+    semver "^5.6.0"
+    sigmund "^1.0.1"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -12247,7 +12262,7 @@ lowlight@~1.9.1:
     fault "^1.0.2"
     highlight.js "~9.12.0"
 
-lru-cache@^4.0.1, lru-cache@^4.1.2:
+lru-cache@^4.0.1, lru-cache@^4.1.2, lru-cache@^4.1.5:
   version "4.1.5"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
   integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
@@ -17134,6 +17149,11 @@ shellwords@^0.1.1:
   resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
   integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
 
+sigmund@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
+  integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=
+
 signal-exit@^3.0.0, signal-exit@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@@ -18603,6 +18623,14 @@ typedoc@^0.15.0:
     typedoc-default-themes "^0.6.0"
     typescript "3.5.x"
 
+typescript-formatter@^7.2.2:
+  version "7.2.2"
+  resolved "https://registry.yarnpkg.com/typescript-formatter/-/typescript-formatter-7.2.2.tgz#a147181839b7bb09c2377b072f20f6336547c00a"
+  integrity sha512-V7vfI9XArVhriOTYHPzMU2WUnm5IMdu9X/CPxs8mIMGxmTBFpDABlbkBka64PZJ9/xgQeRpK8KzzAG4MPzxBDQ==
+  dependencies:
+    commandpost "^1.0.0"
+    editorconfig "^0.15.0"
+
 typescript@3.5.x:
   version "3.5.3"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"

Some files were not shown because too many files changed in this diff