<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Clerk + JavaScript App</title>
<style>
/* Style for the table */
table {
border-collapse: collapse; /* Collapse borders into a single border */
border: 1px solid black; /* Border for the entire table */
}
/* Style for table cells */
td,
th {
border: 1px solid black; /* Border for each cell */
padding: 2px; /* Optional: Add padding to cells */
}
</style>
</head>
<body>
<div id="app"></div>
<p id="error-container" hidden>
Error:
<span id="error-message"></span>
</p>
<h2>Memberships List</h2>
<table id="memberships_table">
<thead>
<tr>
<th>User ID</th>
<th>Identifier</th>
<th>Role</th>
<th id="update-role-head" hidden>Update role</th>
<th id="remove-member-head" hidden>Remove member</th>
</tr>
</thead>
<tbody id="memberships_table_body"></tbody>
</table>
<div id="add-member-container">
<h2>Add member</h2>
<input id="member-user-id" placeholder="Enter member's user ID" />
<button id="add-member">Add member</button>
</div>
<h2>Response:</h2>
<pre id="response"></pre>
<!-- Initialize Clerk with your
Clerk Publishable key and Frontend API URL -->
<script
async
crossorigin="anonymous"
data-clerk-publishable-key="YOUR_PUBLISHABLE_KEY"
src="https://YOUR_FRONTEND_API_URL/npm/@clerk/clerk-js@latest/dist/clerk.browser.js"
type="text/javascript"
></script>
<script>
window.addEventListener('load', async function () {
await Clerk.load()
if (Clerk.user) {
// Check for an active organization
if (Clerk.organization) {
// Render list of organization memberships
async function renderMemberships(organization, isAdmin) {
try {
const { data } = await organization.getMemberships()
const memberships = data
console.log(`getMemberships:`, memberships)
memberships.map((membership) => {
const membershipTable = document.getElementById('memberships_table')
const row = membershipTable.insertRow()
row.insertCell().textContent = membership.publicUserData.userId
row.insertCell().textContent = membership.publicUserData.identifier
row.insertCell().textContent = membership.role
// Add administrative actions:
// Add and remove a member, and update a member's role.
if (isAdmin) {
// Show add, update, remove member buttons
document.getElementById('add-member-container').removeAttribute('hidden')
document.getElementById('update-role-head').removeAttribute('hidden')
document.getElementById('remove-member-head').removeAttribute('hidden')
// Get the user ID of the member
const userId = membership.publicUserData.userId
// Update a member's role
const updateBtn = document.createElement('button')
updateBtn.textContent = 'Change role'
updateBtn.addEventListener('click', async function (e) {
e.preventDefault()
const role = membership.role === 'org:admin' ? 'org:member' : 'org:admin'
await organization
.updateMember({ userId, role })
.then((res) => {
document.getElementById('response').innerHTML = JSON.stringify(res)
})
.catch((error) => {
document.getElementById('error-container').removeAttribute('hidden')
document.getElementById('error-message').innerHTML =
error.errors[0].longMessage
console.log('An error occurred:', error.errors)
})
})
row.insertCell().appendChild(updateBtn)
// Remove a member
const removeBtn = document.createElement('button')
removeBtn.textContent = 'Remove'
removeBtn.addEventListener('click', async function (e) {
e.preventDefault()
await organization
.removeMember(userId)
.then((res) => {
document.getElementById('response').innerHTML = JSON.stringify(res)
})
.catch((error) => {
document.getElementById('error-container').removeAttribute('hidden')
document.getElementById('error-message').innerHTML =
error.errors[0].longMessage
console.log('An error occurred:', error.errors)
})
})
row.insertCell().appendChild(removeBtn)
// Add a new member to the organization
document.getElementById('add-member').addEventListener('click', () => {
const userId = document.getElementById('member-user-id').value
organization
.addMember({ userId, role: 'org:member' })
.then((res) => {
document.getElementById('response').innerHTML = JSON.stringify(res)
})
.catch((error) => {
document.getElementById('error-container').removeAttribute('hidden')
document.getElementById('error-message').innerHTML =
error.errors[0].longMessage
console.log('An error occurred:', error.errors)
})
})
}
})
} catch (error) {
document.getElementById('error-container').removeAttribute('hidden')
document.getElementById('error-message').innerHTML = error.errors[0].longMessage
console.log('An error occurred:', error.errors)
}
}
/**
* Checks if a user is an admin of the
* currently active organization and
* renders the organization's memberships.
*/
async function checkAdminAndRenderMemberships() {
const organizationId = Clerk.organization.id
const { data } = await Clerk.user.getOrganizationMemberships()
const organizationMemberships = data
const currentMembership = organizationMemberships.find(
(membership) => membership.organization.id === organizationId,
)
const currentOrganization = currentMembership.organization
if (!currentOrganization) {
return
}
const isAdmin = currentMembership.role === 'org:admin'
console.log(`Organization:`, currentOrganization)
renderMemberships(currentOrganization, isAdmin)
}
checkAdminAndRenderMemberships()
} else {
// If there is no active organization,
// mount Clerk's <OrganizationSwitcher />
// to allow the user to set an organization as active
document.getElementById('app').innerHTML = `
<h2>Select an organization to set it as active</h2>
<div id="org-switcher"></div>
`
const orgSwitcherDiv = document.getElementById('org-switcher')
Clerk.mountOrganizationSwitcher(orgSwitcherDiv)
}
} else {
document.getElementById('app').innerHTML = `
<div id="sign-in"></div>
`
const signInDiv = document.getElementById('sign-in')
Clerk.mountSignIn(signInDiv)
}
})
</script>
</body>
</html>