Skip to main content

Let visitors run their own company

Goal​

Let the people who sign in to your portal belong to a company — start one, invite colleagues into it, see who is there, change what they may do, and leave — all from a screen you draw.

Before you start​

  • A portal that asks visitors to sign in. See Ask visitors to sign in.
  • Email sending, set up by whoever runs your Parsware installation. An invitation is a message with a link in it; without email nobody can be invited.

Why these are the platform's and not your Custom API​

Everything else your portal reads and writes goes through a Custom API you wrote. A company does not, and it is worth knowing why: who belongs to which company is kept by the platform's identity service, which accepts a visitor's session only from the portal's own server. Your Custom API does not run there — it runs where your tables are — so no script you could write would be allowed to invite anybody.

So the platform gives you the six calls, and you draw the screen. There is no People page built into the platform: the member list, the Invite box and the role picker are yours to lay out, the way the rest of your portal is.

The six calls​

CallWhat it does
startCompany(name)Starts a company with this visitor as its first Owner
invitePerson(email) / invitePerson(email, role)Emails an invitation. Owners only. Member when no role is given
loadCompanyPeople(into) / loadCompanyPeople(into, companyInto)Puts the people into a state field, and the company into a second one
changePersonRole(person, role)Changes what a colleague may do
removePerson(person)Removes a colleague, or withdraws an outstanding invitation
leaveCompany()This visitor leaves

The roles are Member, Billing and Owner, and each may do everything the one before it may. Only Owners invite, change roles and remove people; the platform decides that, not your screen.

Steps​

  1. Draw a text box and bind its value to a state field — say state.text.

  2. Draw a button and give its click handler:

    startCompany(getState("text"));
    createRecord("par_client", { par_name: getState("text") });
    loadCompanyPeople("people", "company");
  3. Draw a second button for inviting:

    invitePerson(getState("text"));
    loadCompanyPeople("people", "company");
    notify("Invitation sent.");
  4. In the screen's screenLoad, put loadCompanyPeople("people", "company"); so the page opens with the list already filled.

The order on the first button is the whole trick

startCompany does two things: it creates the company and re-points this visitor's session at it. Only after that does portalCustomerId() answer anything — which is what the Custom API behind createRecord reads to give your own record the company's id.

Do it the other way round and there is no id to give the record. Your API never receives one from the page, and should not: the platform decides it, which is what stops a visitor claiming somebody else's company.

What lands in your state fields​

loadCompanyPeople("people", "company") fills two fields:

  • state.people — one row per colleague and per outstanding invitation:

    FieldWhat it is
    idmember:… or invitation:… — pass it straight back to removePerson
    nameTheir name, or the address an invitation went to
    emailThe address they sign in with
    roleMember, Billing or Owner
    statusMember or Invited
    isYouWhether this row is the person reading the page
  • state.company — { name, role, canManage }. Bind a caption to state.company.name, and put your Invite box's visible binding on state.company.canManage so an ordinary member is not shown a button they will be refused.

Somebody who is in no company gets an empty list and an empty company, which is not an error — it is what your "start a company" form is for.

A Remove button that works on both kinds of row​

Repeat a table over state.people and put one button in the row:

removePerson(row.id);
loadCompanyPeople("people", "company");

That removes a colleague or withdraws an invitation, because the row's id says which it is. You do not write that condition — and you should not, because the two are different acts to the platform and only it knows the rules for each.

An invitation's role cannot be changed. changePersonRole on an invitation: row is refused in words: withdraw it and invite them again.

What a refusal looks like​

The platform's own sentence appears beside the button somebody pressed — "Only an Owner can invite people", "The last Owner cannot leave the company", "That address is already in this company".

You do not need to write those messages and should not try to pre-empt them: the rules belong to the platform, and a screen that guessed would eventually guess wrong.

The last Owner cannot leave

Somebody who is the only Owner of a company must make a colleague an Owner first. That is deliberate: a company with records behind it is not left with nobody who can administer it by one person pressing a button.

Where an invitation lands​

On your portal's front door, like a confirmation link and a password-reset link. Somebody who is signed in joins straight away; somebody who is not sees the sign-in form with You have been invited above it, and the invitation is still waiting after they sign up and confirm their address. See Ask visitors to sign in.

What this is for​

A company is what a data policy narrows records by. So these two pages fit together: this one is how somebody comes to have a company, and that one is how the rows of a table come to belong to it.