How to add multiple module while creating forge app with UI kit

I have started my working on creating a new app with UI kit. And I got stuck when I have added two modules in my app. Now anything is not working. I have tried many ways and erverytime it is failed. Please help me to resolve the issue to run this.
In this app these are the files that I have updated.

Manifest.yml

modules:
  jira:projectPage:
    - key: newapp-hello-world-project-page
      resource: main
      resolver:
        function: projectPageHandler
      render: native
      title: NewApp

  jira:issuePanel:
    - key: hello-world-issue-panel
      resource: main
      resolver:
        function: issueViewHandler
      render: native

      title: Hello World!
      icon: https://developer.atlassian.com/platform/forge/images/icons/issue-panel-icon.svg
  
  function:
    - key: projectPageHandler
      handler: resolver.projectPageHandler
 
    - key: issueViewHandler
      handler: resolver.issueViewHandler

resources:
  - key: main
    path: src/frontend/index.jsx


app:
  runtime:
    name: nodejs20.x
  id: ari:cloud:ecosystem::app/af2cf143-f784-4f03-a0e4-ae95ee536666

permissions:
  scopes:
    - 'read:jira-work'

Index.jsx

import React, { useEffect, useState } from 'react';
import ForgeReconciler, { Text, useProductContext } from '@forge/react';
import { invoke, requestJira } from '@forge/bridge';



const ProjectPageApp = () => {
  const context = useProductContext();
 
  // State to keep track of the number of issues in the project
  const [issueCount, setIssueCount] = React.useState(null);
  const fetchIssuesForProject = async () => {
    try {
      
      const res = await requestJira(`/rest/api/3/search`);
      const data = await res.json();
 
      if (!res.ok) {
        console.error('Error fetching issues:', res.statusText);
        return;
      }
      return data.total;
    } catch (error) {
      console.error('An unexpected error occurred while fetching issues:', error);
    }
  };
 
  const [data, setData] = useState(null);

  React.useEffect(() => {
    invoke('getText', { example: 'my-invoke-variable' }).then(setData);

    if (context) {
      fetchIssuesForProject().then(setIssueCount);
    }
  }, [context]);

  return (
    <>
      <Text>Number of issues:{issueCount}</Text>
      <Text>{data ? data : 'Loading...'}</Text>
    </>
  );
};

const IssueViewApp = () => {
  const context = useProductContext();
 
  return (
    <div>
      <Text>Hello, world! This is the Issue View module.</Text>
      <Text>Issue Key: {context.extension.issue.key}</Text>
    </div>
  );
};

// Resolver for Project Page
export const projectPageHandler = () => {
  ForgeReconciler.render(<ProjectPageApp />);
};
 
// Resolver for Issue View
export const issueViewHandler = () => {
  ForgeReconciler.render(<IssueViewApp />);
};


resolvers/index.js

import Resolver from '@forge/resolver';
 
const resolver = new Resolver();
 
// Resolver for Project Page Module
resolver.define('projectPageHandler', (req) => {
  // Logic specific to Project Page
  console.log('Project Page Resolver invoked');
  return 'Hello from Project Page!';
});
 
// Resolver for Issue Panel Module
resolver.define('issuePanelHandler', (req) => {
  // Logic specific to Issue Panel
  console.log('Issue Panel Resolver invoked');
  return 'Hello from Issue Panel!';
});
 
// Exporting both handlers
export const projectPageHandler = resolver.getDefinitions();
export const issueViewHandler = resolver.getDefinitions();

src/index.js
export { handler } from './resolvers/index';

Files present in app

Please help me to run this app with both modules.

Hi @ChinmayBorkar2. Welcome to the dev community. I don’t know what you want your app to do, or how you want your source code arranged… but, I’ll help get you unblocked with a working example.

manifest.yml:

modules:
  jira:projectPage:
    - key: hello-world-project-page
      resource: project-page-resource
      resolver:
        function: projectPageHandler
      render: native
      title: Hello Project Page

  jira:issuePanel:
    - key: hello-world-issue-panel
      resource: issue-panel-resource
      resolver:
        function: issuePanelHandler
      render: native

      title: Hello Issue Panel
      icon: https://developer.atlassian.com/platform/forge/images/icons/issue-panel-icon.svg
  
  function:
    - key: projectPageHandler
      handler: index.projectPageHandler
 
    - key: issuePanelHandler
      handler: index.issuePanelHandler

resources:
  - key: project-page-resource
    path: src/frontend/index.jsx
  - key: issue-panel-resource
    path: src/frontend/index.jsx

app:
  runtime:
    name: nodejs20.x
  id: your-app-id-or-run-forge-register

src/index.js

export { issuePanelHandler } from './resolvers';
export { projectPageHandler } from './resolvers';

src/resolvers/index.js

import Resolver from '@forge/resolver';

const resolver = new Resolver();

export const issuePanelHandler = resolver.getDefinitions();
export const projectPageHandler = resolver.getDefinitions();

src/frontend/index.jsx

import React, { useEffect, useState } from 'react';
import ForgeReconciler, { Text, useProductContext } from '@forge/react';

const App = () => {

  const context = useProductContext();
  useEffect(() => {
    if (context) {
      // Take a look at the whole context object in the browser console
      console.log("Context = " + JSON.stringify(context));

      // Below, in the return rendered output, we're just printing out 
      // context.moduleKey, a value that you can use in your logic.
      // You could also modify the manifest to point to a different jsx file
      // for each respective module/resource.
    }
  }, [context]);

  return (
    <>
      <Text>Hello world!</Text>
      <Text>ModuleKey = {context?.moduleKey}</Text>
      <Text>Context JSON = {JSON.stringify(context)}</Text>
    </>
  );
};

ForgeReconciler.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Thanks for your help.
In src/frontend/index.jsx
If I want to show 2 rest api outputs for to two different modules in that case what should we can modify in this code. Please help me out in this.

Means one rest api output for projectpage and second restapi output for issue-panel. So how can we modify this code.

@nmansilla please reply. I need help for creating this.

Hi @chinmayborkar3 ,
I don’t have an example for Jira but I have one for Bitbucket that does something similar and calls 2 different REST APIS to show different content in the bitbucket:workspaceSettingsMenuPage and the bitbucket:repoCodeOverviewCard modules.

You can check out the repository here: forge-bitbucket-reports/01.RepositorySize

The src/index.js file exports the 2 handlers:

export { handleWorskpaceSettings } from './resolvers/workspace-settings';
export { handleRepoCodeOverviewCard } from './resolvers/repo-code-overview-card';

Which are defined in the 2 files in the src/resolvers folder.

This article shows what the app does and how the 2 modules are used.

Cheers,
Caterina

Thanks @ccurti

Can anyone help me with my code that I have shared. How I can fix this?
what are the basic steps that can I follow to use two modules which can show output of two different rest API’s in Jira.
Please help me out.