diff --git a/.github/workflows/cloud-frontend.yml b/.github/workflows/cloud-frontend.yml new file mode 100644 index 0000000000..ee8c59ab4a --- /dev/null +++ b/.github/workflows/cloud-frontend.yml @@ -0,0 +1,75 @@ +name: Deploy to cloud frontend + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to deploy (must start with lts/)' + required: true + default: 'lts/latest' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + + # - name: Check authorization + # run: | + # allowed_user1=${{ secrets.ALLOWED_USER1_USERNAME }} + # allowed_user2=${{ secrets.ALLOWED_USER2_USERNAME }} + # allowed_user3=${{ secrets.ALLOWED_USER3_USERNAME }} + + # if [[ "${{ github.actor }}" != "$allowed_user1" && \ + # "${{ github.actor }}" != "$allowed_user2" && \ + # "${{ github.actor }}" != "$allowed_user3" ]]; then + # echo "❌ User '${{ github.actor }}' is not authorized to trigger this workflow." + # exit 1 + # else + # echo "✅ User '${{ github.actor }}' is authorized." + # fi + + # - name: Validate branch prefix + # run: | + # if [[ "${{ github.event.inputs.branch }}" != lts/* ]]; then + # echo "❌ Branch name must start with 'lts/'" + # exit 1 + # fi + + - name: Checkout code + uses: actions/checkout@v2 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: 22.15.1 + + - name: Install dependencies + run: npm install + + - name: Build the project + run: npm run build:plugins:prod && npm run build:frontend + env: + GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} + NODE_ENV: ${{ secrets.NODE_ENV }} + NODE_OPTIONS: ${{ secrets.NODE_OPTIONS }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + SERVE_CLIENT: ${{ secrets.SERVE_CLIENT }} + SERVER_IP: ${{ secrets.SERVER_IP }} + TJDB_SQL_MODE_DISABLE: ${{ secrets.TJDB_SQL_MODE_DISABLE }} + TOOLJET_SERVER_URL: ${{ secrets.TOOLJET_SERVER_URL }} + TOOLJET_EDITION: cloud + + - name: Deploy to Netlify + run: | + npm install -g netlify-cli + netlify deploy --prod --dir=frontend/build --auth=$NETLIFY_AUTH_TOKEN --site=${{ secrets.CLOUD_NETLIFY_SITE_ID }} + env: + NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} + + - name: ✅ Deployment complete + run: echo "🎉 Deployment to Netlify successful for branch ${{ github.event.inputs.branch }} by ${{ github.actor }}" diff --git a/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx b/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx index f25d815159..05bcd8fbda 100644 --- a/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx +++ b/frontend/src/AppBuilder/AppCanvas/ConfigHandle/ConfigHandle.jsx @@ -7,6 +7,7 @@ import SolidIcon from '@/_ui/Icon/solidIcons/index'; import { ToolTip } from '@/_components/ToolTip'; import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext'; import { DROPPABLE_PARENTS } from '../appCanvasConstants'; +import { Tooltip } from 'react-tooltip'; const CONFIG_HANDLE_HEIGHT = 20; const BUFFER_HEIGHT = 1; @@ -25,6 +26,7 @@ export const ConfigHandle = ({ subContainerIndex, }) => { const { moduleId } = useModuleContext(); + const isLicenseValid = useStore((state) => state.isLicenseValid(), shallow); const shouldFreeze = useStore((state) => state.getShouldFreeze()); const componentName = useStore((state) => state.getComponentDefinition(id, moduleId)?.component?.name || '', shallow); const isMultipleComponentsSelected = useStore( @@ -111,6 +113,9 @@ export const ConfigHandle = ({ } } }} + data-tooltip-id={`invalid-license-modules-${componentName?.toLowerCase()}`} + data-tooltip-html="Your plan is expired.
Renew to use the modules." + data-tooltip-place="right" > {licenseValid && isRestricted && ( @@ -201,6 +206,15 @@ export const ConfigHandle = ({ )} + {/* Tooltip for invalid license on ModuleViewer */} + {!isLicenseValid && componentType === 'ModuleViewer' && ( + + )} ); }; diff --git a/frontend/src/_styles/modules.scss b/frontend/src/_styles/modules.scss index c8410b41a3..239cc3201c 100644 --- a/frontend/src/_styles/modules.scss +++ b/frontend/src/_styles/modules.scss @@ -72,6 +72,10 @@ scrollbar-color: #6a727c4d transparent; } } + &.disabled{ + pointer-events: none; + opacity: 0.5; + } } .empty-module-container{ diff --git a/server/src/modules/app/guards/ability.guard.ts b/server/src/modules/app/guards/ability.guard.ts index 233f0d1e28..a3aff9fe1f 100644 --- a/server/src/modules/app/guards/ability.guard.ts +++ b/server/src/modules/app/guards/ability.guard.ts @@ -63,7 +63,7 @@ export abstract class AbilityGuard implements CanActivate { const licenseRequired: LICENSE_FIELD = featureInfo?.license; if (licenseRequired && !(app?.organizationId || user?.organizationId)) { // If no license is required, continue to the next feature - continue; + return true; } if ( licenseRequired && @@ -78,7 +78,7 @@ export abstract class AbilityGuard implements CanActivate { // If any of the feature is public if (featureInfo.isPublic) { // No other validations if user is API is public - continue; + return true; } if (featureInfo.isSuperAdminFeature && !isSuperAdmin(user)) { @@ -91,7 +91,7 @@ export abstract class AbilityGuard implements CanActivate { if (app?.isPublic && !featureInfo.shouldNotSkipPublicApp) { // No need to do validations if app is public - continue; + return true; } }