The Porting Embed allows a user to complete a number porting inside your app.
Number Porting is split up into a form wizard with multiple steps:
- Carrier Details (Account Number & PIN)
- Account Holder Details (First Name, Last Name & Birthday)
- Account Holder Address
- Donor Provider Approval
Each step will only be shown if the porting requires these fields, and if the user did not fill out these fields yet. When the user submits a step, the porting will be updated, and the user will see the next step, until all fields were filled out.
Important
The Porting Embed currently only supports portings in the status of informationRequired
.
porting-embed-demo.mp4
The embed mounts into an element on your site. Add a <div id="portingEmbed">
to your site wherever the embed should be mounted to.
The embed does not include a submit button. You need to add your own button and use form="gigsPortingEmbedForm"
. You have full control over the customization of this button.
You have to specify your Gigs Project ID when initializing the embed.
<div id="portingEmbed"></div>
<button id="gigsPortingEmbedForm">Submit</button>
// Optional. Adds styling to the embed.
import '@gigscom/embeds-js/style.css'
import { PortingEmbed } from '@gigscom/embeds-js'
// Fetch a new ConnectSession from an internal API endpoint, or any other way
// to obtain a new ConnectSession.
const session = await fetchSessionForCurrentUser()
// Initialize the embed with the session.
const embed = await PortingEmbed(session, { project: 'your-project-id' })
embed.mount('#portingEmbed')
// you can use a string selector or an element reference.
// Once the porting is completed, you can redirect the user to the next page.
embed.on('completed', ({ porting }) => {
location.href = 'https://your-site.com/porting-completed'
})
See src/PortingEmbedExample.tsx
for a full example implementation.
- Demo
- Minimal example
- Example app
- Usage
- Show a loading spinner while the embed is initializing
- Show UI depending on the current step
- Use the included styling
- CSS customization
- Translations and text customization
- Continue after all fields were filled out
- Show a loading state while the form is submitted
- Reacting to validation changes
- Unsubscribe from event listeners
- Using a different form id
- Updating options
- Unmount the embed
- Usage with React
- Reference
The embed does not show an initial loading state. You can show your own loading state and hide it immediately before you mount()
the embed.
const embed = await PortingEmbed(session)
document.querySelector('.loading').remove()
embed.mount('#portingEmbed')
You can display custom headlines and information in your app depending on the current step of the porting wizard, by using currentStep()
and the stepChange
event.
const embed = await PortingEmbed(session)
let step = embed.currentStep()
embed.on('stepChange', ({ nextStep, prevStep }) => {
step = nextStep
})
See also:
embed.currentStep()
embed.on('stepChange')
- Step for the list of steps
The embed ships with default styles which you can import in your app.
import '@gigscom/embeds-js/style.css'
The embed allows for full customization of CSS.
const embed = await PortingEmbed(session, {
options: {
className: {
form: ({ name, dirty, valid, submitting, touched }) => 'your-class-name',
field: ({ name, dirty, valid, touched }) => 'your-class-name',
input: ({ name, dirty, valid, touched }) => 'your-class-name',
label: ({ name, dirty, valid, touched }) => 'your-class-name',
error: ({ name, dirty, touched }) => 'your-class-name',
}
}
})
form
corresponds to the<form>
element.field
corresponds to the<div>
around the input, label and error.input
corresponds to the<input>
element.label
corresponds to the<label>
element.error
corresponds to the<div>
element which is shown when there is a validation error.
To apply different styles based on the current state of the embed, the className is a function with parameters:
name
is the name of the input. For the form, it's the name of the current wizard step.dirty
indicates if the input or form was changed (i.e. the user changed an input).touched
indicates if the input or form was touched (i.e. the user clicked on an input).valid
indicates if the input or form is valid or invalid.submitting
indicates if the form is currently submitting.
See also:
- Step for the list of steps.
- Field Names for the list of field names.
The embed ships with default text in english, but you can override any text that is rendered by the embed, if you want to support a different language or customize the text otherwise.
You don't have to provide all text overrides. Your overrides will be merged into the default text.
const embed = await PortingEmbed(session, {
options: {
text: {
'field.firstName.label': 'Vorname',
'field.firstName.error.required': 'Muss ausgefüllt werden',
'field.lastName.label': 'Nachname',
'field.lastName.error.required': 'Muss ausgefüllt werden',
'field.birthday.label': 'Geburtsdatum',
// ...
}
}
})
See also:
- Text for a list of all text overrides.
When the user entered all fields in all steps of the wizard, the embed will not render any UI anymore. You can handle this in your app by listening for the completed
event.
const embed = await PortingEmbed(session)
embed.on('completed', ({ porting }) => {
location.href = 'https://your-site.com/porting/completed'
})
Note
A completed porting does not mean the porting was also successful. It takes some time until the porting was processed by the provider. You can subscribe to our webhooks to be notified when a porting succeeds or is denied.
See also:
You can show a loading spinner, for example on your button, while the form is being submitted, by listening to the submitStatus
event.
Tip
It's useful to disable the button while the form is submitting.
const embed = await PortingEmbed(session)
embed.on('submitStatus', ({ status, porting, error }) => {
if (status === 'loading') {
document.querySelector('.submit').innerText = 'Loading...'
document.querySelector('.submit').disabled = true
} else {
document.querySelector('.submit').innerText = 'Save'
document.querySelector('.submit').disabled = false
}
})
status
can be one of the following:
loading
while the request is loading.success
when the request was successful. The updated porting is available asporting
on the event.error
when the request failed. The error is available aserror
on the event.
See also:
You can react to changes if the current form is valid or invalid by listening to the validationChange
event:
const embed = await PortingEmbed(session)
embed.on('validationChange', ({ isValid }) => {
if (isValid) {
document.querySelector('.submit').style.opacity = 1;
} else {
document.querySelector('.submit').style.opacity = 0.5;
}
})
The form will initially always be valid, before the user interacts with the form.
Important
Do not disable the submit button based on the validation. Clicking the submit button is a way a user should be able to trigger form validation.
See also:
If you listen to an event using embed.on()
, you can use embed.off()
to remove the event listener again.
const embed = await PortingEmbed(session)
const completed = () => { /* ... */ }
embed.on('completed', completed)
embed.off('completed', completed)
See also:
By default, the form and your corresponding button use the ID gigsPortingEmbedForm
. You can change this value when initializing the embed:
const embed = await PortingEmbed(session, {
options: {
formId: 'customFormId'
}
})
See also:
You can update the options after the embed was initialized, by using the update()
. This does not merge the new options into the existing options, it overrides them completely.
const embed = await PortingEmbed(session, {
options: {
formId: 'customFormId'
}
})
embed.update({ formId: 'newCustomFormId' })
See also:
You can use unmount()
to unmount the embed from the DOM, for example when you're building a Single Page Application.
const embed = await PortingEmbed(session)
app.onBackNavigation(() => {
embed.unmount()
})
See also:
export function App() {
const $mount = useRef<HTMLDivElement>(null)
const [status, setStatus] = useState('initializing')
const [step, setStep] = useState(null)
const [embed, setEmbed] = useState<PortingEmbedInstance>()
useEffect(() => {
async function main() {
const session = await fetch('/connectSessionForCurrentPorting')
.then(res => res.json())
const embed = await PortingEmbed(session)
setEmbed(embed)
}
main()
}, [])
useEffect(() => {
if (!embed) return
setStatus('loaded')
setStep(embed.currentStep())
embed.mount($mount.current)
const handleStepChange = ({ nextStep }) => setStep(nextStep)
const handleCompleted = ({ porting }) => {
router.push(`/completed?porting=${porting.id}`)
}
const handleSubmitStatus = ({ status }) => {
if (status === 'loading') {
setStatus('submitting')
} else {
setStatus('loading')
}
}
embed.on('stepChange', handleStepChange)
embed.on('completed', handleCompleted)
embed.on('submitStatus', handleSubmitStatus)
return () => {
embed.off('stepChange', handleStepChange)
embed.off('completed', handleCompleted)
embed.off('submitStatus', handleSubmitStatus)
embed.unmount()
}
}, [embed])
return (
<>
<StepTitle step={step} />
{status === 'initializing' && <Loading />}
<div ref={$mount} />
<Button
form="gigsPortingEmbedForm"
loading={status === 'submitting'}
>
Submit
</button>
<>
)
}
const embed = await PortingEmbed(connectSession, {
project,
options,
})
Required.
The complete Connect Session object which you created on your server and passed to the browser. Do not store this.
Required.
The ID of your Gigs Project.
Optional. Options to customize the embed. See the usage above.
Mounts the embed into the element. element
can be a string selector or an element reference.
Updates the options
which the embed was initialized with. It overrides all options and does not merge the new options with the existing options.
Unmounts the embed from the DOM.
Add an event listener to the embed. You can use the following events:
// Fired when the porting was completed.
embed.on('completed', ({ porting }) => {})
// Fired when the porting step was successfully submitted and
// continues with the next step.
embed.on('stepChange', ({ nextStep, prevStep }) => {})
// Fired when the submit status of the porting changes.
embed.on('submitStatus', ({ status, porting, error }) => {
// status can be `loading`, `success` or `error`
})
// Fired when the form gets invalid or valid.
embed.on('validationChange', ({ isValid }) => {})
Remove an event listener.
Gets the name of the current step of the wizard.
The porting form is divided into a multi-step wizard. Each step in the wizard has a name:
"carrierDetails"
for the Carrier Details step"holderDetails"
for the Account Holder Details step"address"
for the Account Holder Address step"donorProviderApproval"
for the Donor Provider Approval stepnull
if there are no steps left, meaning all fields were filled out.
Each field in the a step has a name. If a field is not required by a porting, it will not be shown.
- Form:
carrierDetails
accountNumber
(text)accountPin
(text)
- Form:
holderDetails
firstName
(text)lastName
(text)birthday
(date)
- Form:
address
line1
(text)line2
(text)city
(text)postalCode
(text)state
(text)country
(text)
- Form:
donorProviderApproval
donorProviderApproval
(checkbox)
Key | Default |
---|---|
field.accountNumber.label |
Account Number |
field.accountNumber.error.required |
The account number is required |
field.accountPin.label |
Account PIN |
field.accountPin.error.required |
The account pin is required |
field.accountPin.error.cleared |
The new account pin is empty. If you do not want to change the account pin, clear the input. |
field.firstName.label |
First Name |
field.firstName.error.required |
Your first name is required |
field.lastName.label |
Last Name |
field.lastName.error.required |
Your last name is required |
field.birthday.label |
Birthday |
field.birthday.error.required |
Your birthday is required |
field.line1.label |
Line 1 |
field.line1.error.required |
Line 1 is required |
field.line2.label |
Line 2 |
field.city.label |
City |
field.city.error.required |
City is required |
field.postalCode.label |
Postal Code |
field.postalCode.error.required |
Postal Code is required |
field.state.label |
State (ISO code) |
field.state.error.format |
Must be an ISO state code |
field.country.label |
Country (2 letter code) |
field.country.error.required |
Country is required |
field.country.error.format |
Must be an ISO country code |
field.donorProviderApproval.label |
I have notified my current provider of the number porting and got the approval that the number can be ported |
field.donorProviderApproval.error.required |
You must get the approval of your current provider |