Skip to content

Commit

Permalink
feat: Adds Licensing upload tab
Browse files Browse the repository at this point in the history
get value from image

Signed-off-by: Tiago Fonseca <[email protected]>

fix

Signed-off-by: Tiago Fonseca <[email protected]>

addressing comments

addressing comments
  • Loading branch information
tiagoapolo committed Jan 17, 2025
1 parent 2e79ff4 commit 4d88538
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 6 deletions.
50 changes: 50 additions & 0 deletions frontend/common/services/useOrganisationLicensing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Res } from 'common/types/responses'
import { Req } from 'common/types/requests'
import { service } from 'common/service'

export const organisationLicensingService = service
.enhanceEndpoints({ addTagTypes: ['OrganisationLicensing'] })
.injectEndpoints({
endpoints: (builder) => ({
uploadOrganisationLicence: builder.mutation<
Res['organisationLicence'],
Req['uploadOrganisationLicence']
>({
query: (query: Req['uploadOrganisationLicence']) => {
const formData = new FormData()
formData.append('licence_signature', query.body.licence_signature)
formData.append('licence', query.body.licence)
return {
body: formData,
method: 'PUT',
url: `organisations/${query.id}/licence`,
}
},
}),
// END OF ENDPOINTS
}),
})

export async function uploadOrganisationLicence(
store: any,
data: Req['uploadOrganisationLicence'],
options?: Parameters<
typeof organisationLicensingService.endpoints.uploadOrganisationLicence.initiate
>[1],
) {
store.dispatch(
organisationLicensingService.endpoints.uploadOrganisationLicence.initiate(
data,
options,
),
)
return Promise.all(
store.dispatch(organisationLicensingService.util.getRunningQueriesThunk()),
)
}
// END OF FUNCTION_EXPORTS

export const {
useUploadOrganisationLicenceMutation,
// END OF EXPORTS
} = organisationLicensingService
7 changes: 7 additions & 0 deletions frontend/common/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ export type Req = {
environments?: string
}>
getOrganisations: {}
uploadOrganisationLicence: {
id: number
body: {
licence_signature: File
licence: File
}
}
getProjects: {
organisationId: string
}
Expand Down
1 change: 1 addition & 0 deletions frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@ export type Res = {
segments: PagedResponse<Segment>
segment: Segment
auditLogs: PagedResponse<AuditLogItem>
organisationLicence: {}
organisations: PagedResponse<Organisation>
projects: ProjectSummary[]
project: Project
Expand Down
7 changes: 2 additions & 5 deletions frontend/common/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -354,10 +354,7 @@ const Utils = Object.assign({}, require('./base/_utils'), {
if (plan && plan.includes('start-up')) {
return planNames.startup
}
if (
global.flagsmithVersion?.backend.is_enterprise ||
(plan && plan.includes('enterprise'))
) {
if (Utils.isEnterpriseImage() || (plan && plan.includes('enterprise'))) {
return planNames.enterprise
}
return planNames.free
Expand Down Expand Up @@ -555,6 +552,7 @@ const Utils = Object.assign({}, require('./base/_utils'), {
getViewIdentitiesPermission() {
return 'VIEW_IDENTITIES'
},
isEnterpriseImage: () => global.flagsmithVersion?.backend.is_enterprise,
isMigrating() {
const model = ProjectStore.model as null | ProjectType
if (
Expand All @@ -566,7 +564,6 @@ const Utils = Object.assign({}, require('./base/_utils'), {
return false
},
isSaas: () => global.flagsmithVersion?.backend?.is_saas,

isValidNumber(value: any) {
return /^-?\d*\.?\d+$/.test(`${value}`)
},
Expand Down
110 changes: 110 additions & 0 deletions frontend/web/components/LicensingTabContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { useUploadOrganisationLicenceMutation } from 'common/services/useOrganisationLicensing'
import React, { useEffect, useRef, useState } from 'react'
import Button from './base/forms/Button'
import Utils from 'common/utils/utils'

type LicensingTabContentProps = {
organisationId: number
}

const LicensingTabContent: React.FC<LicensingTabContentProps> = ({
organisationId,
}) => {
const [uploadOrganisationLicence, { error, isLoading, isSuccess }] =
useUploadOrganisationLicenceMutation()

const [licence, setLicence] = useState<File | null>(null)
const [licenceSignature, setLicenceSignature] = useState<File | null>(null)

const licenceInputRef = useRef<HTMLInputElement>(null)
const licenceSignatureInputRef = useRef<HTMLInputElement>(null)

useEffect(() => {
if (isSuccess) {
toast('Licence uploaded successfully')
}

if (!isSuccess && error?.data) {
toast(
Array.isArray(error?.data)
? error?.data[0]
: 'Upload was not successful',
'danger',
)
}
}, [isSuccess, error])

const handleUpload = () => {
if (!licence || !licenceSignature) return
uploadOrganisationLicence({
body: { licence, licence_signature: licenceSignature },
id: organisationId,
})
}

return (
<div className='mt-4'>
<h5 className='mb-5'>Upload Licensing Files</h5>
<form
className='upload-licensing-tab'
onSubmit={(e) => {
Utils.preventDefault(e)
handleUpload()
}}
>
<FormGroup>
<input
type='file'
ref={licenceInputRef}
style={{ display: 'none' }}
onChange={() =>
setLicence(licenceInputRef.current?.files?.[0] ?? null)
}
/>
<input
type='file'
ref={licenceSignatureInputRef}
style={{ display: 'none' }}
onChange={() =>
setLicenceSignature(
licenceSignatureInputRef.current?.files?.[0] ?? null,
)
}
/>
<div className='flex-row'>
<Button onClick={() => licenceInputRef.current?.click()}>
Select Licence File
</Button>
{!!licence?.name && (
<p className='mt-auto mb-auto ml-2 fs-small lh-sm'>
{licence.name}
</p>
)}
</div>
<div className='flex-row mt-4'>
<Button onClick={() => licenceSignatureInputRef.current?.click()}>
Select Signature File
</Button>
{!!licenceSignature?.name && (
<p className='mt-auto mb-auto ml-2 fs-small lh-sm'>
{licenceSignature.name}
</p>
)}
</div>
<div className='text-right'>
<Button
type='submit'
data-test='create-feature-btn'
id='create-feature-btn'
disabled={!licence || !licenceSignature}
>
{isLoading ? 'Uploading' : 'Upload Licensing Files'}
</Button>
</div>
</FormGroup>
</form>
</div>
)
}

export default LicensingTabContent
14 changes: 13 additions & 1 deletion frontend/web/components/pages/OrganisationSettingsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ import PageTitle from 'components/PageTitle'
import SamlTab from 'components/SamlTab'
import Setting from 'components/Setting'
import AccountProvider from 'common/providers/AccountProvider'
import LicensingTabContent from 'components/LicensingTabContent'
import Utils from 'common/utils/utils'

const SettingsTab = {
'Billing': 'billing',
'General': 'general',
'Keys': 'keys',
'Licensing': 'licensing',
'SAML': 'saml',
'Usage': 'usage',
'Webhooks': 'webhooks',
Expand Down Expand Up @@ -228,7 +231,7 @@ const OrganisationSettingsPage = class extends Component {
const { chargebee_email } = subscriptionMeta || {}

const displayedTabs = []

const isEnterprise = Utils.isEnterpriseImage()
if (
AccountStore.getUser() &&
AccountStore.getOrganisationRole() === 'ADMIN'
Expand All @@ -237,6 +240,7 @@ const OrganisationSettingsPage = class extends Component {
...[
SettingsTab.General,
paymentsEnabled && !isAWS ? SettingsTab.Billing : null,
isEnterprise ? SettingsTab.Licensing : null,
SettingsTab.Keys,
SettingsTab.Webhooks,
SettingsTab.SAML,
Expand Down Expand Up @@ -463,6 +467,14 @@ const OrganisationSettingsPage = class extends Component {
</TabItem>
)}

{displayedTabs.includes(SettingsTab.Licensing) && (
<TabItem tabLabel='Licensing'>
<LicensingTabContent
organisationId={organisation.id}
/>
</TabItem>
)}

{displayedTabs.includes(SettingsTab.Keys) && (
<TabItem tabLabel='API Keys'>
<AdminAPIKeys organisationId={organisation.id} />
Expand Down

0 comments on commit 4d88538

Please sign in to comment.