Skip to content
36 changes: 26 additions & 10 deletions src/generators/node/appExtensions/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ function viteConfigContent(): string {
export default defineConfig({
root,
base: '/extensions/',
// Load .env from the project root so VITE_* variables are available
envDir: '../../',
plugins: [react()],
build: {
outDir: 'dist',
Expand Down Expand Up @@ -172,15 +174,14 @@ function frontendTsConfig(): Record<string, unknown> {
}

function panelComponentContent(hasModal: boolean): string {
const sdkImportLine = hasModal ? "import { Command, Modal } from '@pipedrive/app-extensions-sdk';" : '';
const runSdkActionDestructure = hasModal ? ', runSdkAction' : '';
const openModalHandler = hasModal
? dedent`
async function openCustomModal(): Promise<void> {
await runSdkAction('Custom modal opened', (client) =>
await runSdkAction('Modal opened', (client) =>
client.execute(Command.OPEN_MODAL, {
type: Modal.CUSTOM_MODAL,
action_id: 'custom-modal',
// Replace with your Custom Modal's "Extension identifier" from Marketplace Developer Hub → App Extensions
action_id: import.meta.env.VITE_CUSTOM_MODAL_ACTION_ID,
Comment on lines 181 to +184
data: { source: 'panel' },
}),
);
Expand All @@ -195,11 +196,10 @@ function panelComponentContent(hasModal: boolean): string {
`
: '';

const sdkImportPrefix = sdkImportLine ? sdkImportLine + '\n' : '';

return dedent`
import { useEffect } from 'react';
${sdkImportPrefix}import { usePipedriveSdk } from '../shared/pipedriveSdk';
import { Command, Modal } from '@pipedrive/app-extensions-sdk';
import { usePipedriveSdk } from '../shared/pipedriveSdk';

function formatQueryValue(key: string, value: string): string {
return /token|secret|code/i.test(key) ? 'Present' : value;
Expand All @@ -212,14 +212,27 @@ function panelComponentContent(hasModal: boolean): string {
}

export default function Panel() {
const { context, status, theme, visibility, pageState, lastAction, signedTokenPreview, isReady${runSdkActionDestructure}, actions } =
const { context, status, theme, visibility, pageState, lastAction, signedTokenPreview, isReady, runSdkAction, actions } =
usePipedriveSdk('panel');
const queryEntries = Object.entries(context.query);

useEffect(() => {
document.title = 'Custom Panel';
}, []);

async function logActivity(): Promise<void> {
const dealId = context.query.selectedIds ? parseInt(context.query.selectedIds) : undefined;
await runSdkAction('Activity logged', (client) =>
client.execute(Command.OPEN_MODAL, {
type: Modal.ACTIVITY,
Comment on lines +226 to +227
prefill: {
subject: 'Follow-up',
...(dealId ? { deal: dealId } : {}),
},
}),
);
}

${openModalHandler}

if (!isReady && status !== 'Local preview') {
Expand Down Expand Up @@ -266,14 +279,17 @@ function panelComponentContent(hasModal: boolean): string {
</section>

<section className="toolbar" aria-label="SDK actions">
<button type="button" disabled={!isReady} onClick={logActivity}>
Log activity
</button>
<button type="button" disabled={!isReady} onClick={actions.showSnackbar}>
Snackbar
</button>
<button type="button" className="secondary" disabled={!isReady} onClick={actions.showConfirmation}>
Confirm
</button>
<button type="button" className="ghost" disabled={!isReady} onClick={actions.resize}>
Resize panel
{actions.isExpanded ? 'Collapse panel' : 'Expand panel'}
</button>
<button type="button" className="ghost" disabled={!isReady} onClick={actions.getSignedToken}>
Get token
Expand Down Expand Up @@ -403,7 +419,7 @@ function modalComponentContent(): string {
Confirm
</button>
<button type="button" className="ghost" disabled={!isReady} onClick={actions.resize}>
Resize modal
{actions.isExpanded ? 'Collapse modal' : 'Expand modal'}
</button>
<button type="button" className="ghost" disabled={!isReady} onClick={actions.getSignedToken}>
Get token
Expand Down
15 changes: 12 additions & 3 deletions src/generators/node/appExtensions/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,18 @@ export function sdkWrapperContent(): string {
if (response) setLastAction(response.confirmed ? 'Confirmed' : 'Cancelled');
}, [runSdkAction]);

const [isExpanded, setIsExpanded] = useState(false);

const resize = useCallback(async () => {
const size = surface === 'modal' ? { height: 480, width: 720 } : { height: 420 };
await runSdkAction('Resize requested', (client) => client.execute(Command.RESIZE, size));
}, [runSdkAction, surface]);
const nextExpanded = !isExpanded;
const size = surface === 'modal'
? (nextExpanded ? { height: 480, width: 720 } : { height: 420, width: 640 })
: (nextExpanded ? { height: 420 } : { height: 360 });
await runSdkAction(nextExpanded ? 'Expanded' : 'Collapsed', (client) =>
client.execute(Command.RESIZE, size),
);
setIsExpanded(nextExpanded);
}, [runSdkAction, surface, isExpanded]);

Comment on lines +197 to 206
const getSignedToken = useCallback(async () => {
const response = await runSdkAction('Signed token received', (client) => client.execute(Command.GET_SIGNED_TOKEN));
Expand All @@ -220,6 +228,7 @@ export function sdkWrapperContent(): string {
showConfirmation,
resize,
getSignedToken,
isExpanded,
},
};
}
Expand Down
1 change: 1 addition & 0 deletions src/generators/node/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,7 @@ function buildAppExtensionUiService(): ComposeService {
build: { context: '.', dockerfile: 'Dockerfile.app-extension-ui' },
user: 'root',
command: nodeVolumeCommand(`${quietInstallCommand()} && npm run dev:frontend`),
env_file: ['.env'],
environment: { CHOKIDAR_USEPOLLING: 'true' },
ports: ['5173:5173'],
volumes: ['./package.json:/app/package.json:ro', 'app_extension_ui_node_modules:/app/node_modules'],
Expand Down
2 changes: 2 additions & 0 deletions src/generators/node/projectBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ function appExtensionEnvExample(options: GeneratorOptions): string {
'# Custom modal iframe URLs:',
'# Local: https://<your-vite-tunnel>/extensions/modal',
'# Production: https://<your-backend-domain>/extensions/modal',
'VITE_CUSTOM_MODAL_ACTION_ID=',
'# Paste the "Extension identifier" from Marketplace Developer Hub → App Extensions',
);
}

Expand Down
Loading