ダッシュボード タブ アプリを構築する

ダッシュボードは、データを追跡、分析、表示して、組織または特定のプロセスの分析情報を得るためのツールです。 Teams のダッシュボードを使用すると、重要なメトリックを監視および表示できます。

Teams Toolkit のダッシュボード タブ テンプレートを使用すると、Teams のコンテンツの概要を提供する複数のカードとキャンバスの統合を開始できます。 次の操作を行うことができます:

  • ウィジェットを使用して、ダッシュボード タブ内のアプリやサービスのコンテンツを表示します。
  • Graph API とアプリを統合して、選択したデータの実装に関する詳細を視覚化します。
  • カスタマイズ可能なダッシュボードを作成して、複数の領域と部門間で表示する必要がある情報を追跡するのに役立つ特定の目標を設定できます。

ダッシュボードのサンプルを示すスクリーンショット。

チームは、Teams ダッシュボード タブ アプリを使用して、Teams のさまざまなソースから最新の更新プログラムを取得できます。 ダッシュボード タブ アプリを使用して、多数のメトリック、データ ソース、API、サービスを接続します。 ダッシュボード タブ アプリは、ビジネスがソースから関連情報を抽出し、ユーザーに提示するのに役立ちます。 ダッシュボード タブ アプリの作成の詳細については、「 ステップ バイ ステップ ガイド」を参照してください。

新しいダッシュボードを追加する

ダッシュボード タブ アプリを作成したら、新しいダッシュボードを追加できます。

新しいダッシュボードを追加するには、次の手順に従います。

  1. ダッシュボード クラスを作成する
  2. メソッドをオーバーライドしてダッシュボード タブ アプリをカスタマイズする
  3. 新しいダッシュボード タブ アプリのルートを追加する
  4. マニフェストを変更して新しいダッシュボード タブ アプリを追加する

ダッシュボード クラスを作成する

ダッシュボードの .tsx 拡張子を持つファイルを src/dashboards ディレクトリに作成します (例: YourDashboard.tsx)。 次に、 を拡張するクラスを作成します。 BaseDashboard class from
@microsoft/teamsfx-react

//YourDashboard.tsx
import { BaseDashboard } from "@microsoft/teamsfx-react";

export default class YourDashboard extends BaseDashboard<any, any> {}

注:

すべてのメソッドは省略可能です。 メソッドをオーバーライドしない場合は、既定のダッシュボード レイアウトが使用されます。

メソッドをオーバーライドしてダッシュボード タブ アプリをカスタマイズする

BaseDashboard クラスには、ダッシュボード レイアウトをカスタマイズするためにオーバーライドできるメソッドがいくつか用意されています。 次の表に、オーバーライドできるメソッドの一覧を示します。

メソッド Function
styling() ダッシュボードのスタイルをカスタマイズします。
layout() ウィジェットのレイアウトを定義します。

次のコードは、ダッシュボード レイアウトをカスタマイズする例です。

.your-dashboard-layout {
  grid-template-columns: 6fr 4fr;
}
import { BaseDashboard } from "@microsoft/teamsfx-react";
import ListWidget from "../widgets/ListWidget";
import ChartWidget from "../widgets/ChartWidget";

export default class YourDashboard extends BaseDashboard<any, any> {
  override styling(): string {
    return "your-dashboard-layout";
  }

  override layout(): JSX.Element | undefined {
    return (
      <>
        <ListWidget />
        <ChartWidget />
      </>
    );
  }
}

新しいダッシュボード タブ アプリのルートを追加する

ウィジェットをデータ ソース ファイルにリンクする必要があります。 ウィジェットは、ソース ファイルからダッシュボードに表示されるデータを取得します。

src/App.tsx ファイルを開き、新しいダッシュボードのルートを追加します。 次に例を示します:

import YourDashboard from "./dashboards/YourDashboard";

export default function App() {
  ...
  <Route path="/yourdashboard" element={<YourDashboard />} />
  ...
}

マニフェストを変更して新しいダッシュボード タブ アプリを追加する

appPackage/manifest.json ファイルを開き、[staticTabs] の下に新しいダッシュボード タブを追加します。 詳細については、「アプリ マニフェスト」を参照してください。 次に例を示します:

{
  "entityId": "index1",
  "name": "Your Dashboard",
  "contentUrl": "${{TAB_ENDPOINT}}/index.html#/yourdashboard",
  "websiteUrl": "${{TAB_ENDPOINT}}/index.html#/yourdashboard",
  "scopes": ["personal"]
}

ダッシュボード レイアウトをカスタマイズする

TeamsFx には、ダッシュボードのレイアウトを定義および変更するための便利なメソッドが用意されています。 メソッドを次に示します。

  • 高さが 350 px の行の 3 つのウィジェットがそれぞれ幅の 20%、60%、および 20% を占めます。

    .customize-class-name {
      grid-template-rows: 350px;
      grid-template-columns: 2fr 6fr 2fr;
    }
    
    export default class SampleDashboard extends BaseDashboard<any, any> {
    
      override styling(): string {
        return "customize-class-name";
      }
    
      override layout(): JSX.Element | undefined {
        return (
          <>
           <ListWidget />
           <ChartWidget />
           <NewsWidget />
          </>
        );
      }
    }
    

    カスタマイズされたダッシュボード レイアウトを示すスクリーンショット。

  • 幅が 600 px と 1100 px の行の 2 つのウィジェット。 最初の行の高さはコンテンツの最大高さであり、2 行目の高さは 400 px です。

    .customize-class-name {
      grid-template-rows: max-content 400px;
      grid-template-columns: 600px 1100px;
    }
    
        export default class SampleDashboard extends Dashboard {
      override styling(): string {
        return "customize-class-name";
      }
    
      override layout(): JSX.Element | undefined {
        return (
          <>
        <ListWidget />
        <ChartWidget />
        <NewsWidget />
          </>
        );
      }
        }
    

    ダッシュボード レイアウトの高さと幅のカスタマイズを示すスクリーンショット。

  • 列に 2 つのウィジェットを配置します。

    .one-column {
        display: grid;
        gap: 20px;
        grid-template-rows: 1fr 1fr;
      }
    
      export default class SampleDashboard extends BaseDashboard<any, any> {
        override layout(): JSX.Element | undefined {
          return (
            <>
              <NewsWidget />
              <div className="one-column">
                <ListWidget />
                <ChartWidget />          
              </div>
            </>
          );
        }
      }
    

    2 ウィジェットのカスタマイズを示すスクリーンショット。

ダッシュボード タブ アプリの抽象化

ダッシュボードのレイアウトを調整するために、TeamsFx には、開発者がダッシュボードを実装するための BaseDashboard クラスが用意されています。

次のコードは、 BaseDashboard クラスの例です。

function dashboardStyle(isMobile?: boolean) {
  return mergeStyles({
    display: "grid",
    gap: "20px",
    padding: "20px",
    gridTemplateRows: "1fr",
    gridTemplateColumns: "4fr 6fr",
    ...(isMobile === true ? { gridTemplateColumns: "1fr", gridTemplateRows: "1fr" } : {}),
  });
}

interface BaseDashboardState {
  isMobile?: boolean;
  showLogin?: boolean;
  observer?: ResizeObserver;
}

export class BaseDashboard<P, S> extends Component<P, S & BaseDashboardState> {
  private ref: React.RefObject<HTMLDivElement>;

  public constructor(props: Readonly<P>) {
    super(props);
    this.state = {
      isMobile: undefined,
      showLogin: undefined,
      observer: undefined,
    } as S & BaseDashboardState;
    this.ref = React.createRef<HTMLDivElement>();
  }

  public async componentDidMount() {
    const observer = new ResizeObserver((entries) => {
      for (const entry of entries) {
        if (entry.target === this.ref.current) {
          const { width } = entry.contentRect;
          this.setState({ isMobile: width < 600 } as S & BaseDashboardState);
        }
      }
    });
    observer.observe(this.ref.current!);
  }

  public componentWillUnmount(): void {
    if (this.state.observer && this.ref.current) {
      this.state.observer.unobserve(this.ref.current);
    }
  }

  public render() {
    return (
      <div
        ref={this.ref}
        className={mergeStyles(dashboardStyle(this.state.isMobile), this.styling())}
      >
        {this.layout()}
      </div>
    );
  }

  protected layout(): JSX.Element | undefined {
    return undefined;
  }

  protected styling(): string {
    return null;
  }
}

BaseDashboard クラスでは、TeamsFx はカスタマイズ可能なメソッドを使用して基本的なレイアウトを提供します。 ダッシュボードは引き続き React コンポーネントであり、TeamsFx は、次のような React コンポーネントのライフサイクルに基づいて関数の基本的な実装を提供します。

  • グリッド レイアウトに基づく基本的なレンダリング ロジックの実装。
  • モバイル デバイスに自動的に適応するためのオブザーバーの追加。

オーバーライドするカスタマイズ可能なメソッドを次に示します。

メソッド 機能 オーバーライドすることをお勧めします
constructor() ダッシュボードの状態と変数を初期化します。 いいえ
componentDidMount() コンポーネントのマウント後に を呼び出します。 いいえ
componentWillUnmount() コンポーネントがマウント解除されたときにを呼び出します。 いいえ
render() 更新がある場合にを呼び出します。 ダッシュボードの既定のレイアウトは、このメソッドで定義されています。 いいえ
layout ダッシュボード内のウィジェットのレイアウトを定義します。 このメソッドはオーバーライドできます。 はい
styling() ダッシュボードのスタイルをカスタマイズするには。 このメソッドはオーバーライドできます。 はい

ダッシュボードでウィジェットを使用する

ウィジェットは、構成可能な情報とグラフをダッシュボードに表示します。 ウィジェットボードに表示され、関心を反映するようにウィジェットをピン留め、ピン留め解除、配置、サイズ変更、カスタマイズできます。 ウィジェットボードは、使用状況に基づいて関連するウィジェットとパーソナライズされたコンテンツを表示するように最適化されています。

ウィジェットをカスタマイズする

ウィジェットをカスタマイズするには、 BaseWidget クラスで次のメソッドをオーバーライドします。

  • ウィジェットをカスタマイズするには、 header()body()、および footer() をオーバーライドします。

    export class NewsWidget extends BaseWidget<any, any> {
    override header(): JSX.Element | undefined {
    return (
      <div>
        <News28Regular />
        <Text>Your News</Text>
        <Button icon={<MoreHorizontal32Regular />} appearance="transparent" />
      </div>
    );
    }
    
    override body(): JSX.Element | undefined {
    return (
      <div>
        <Image src="image.svg" />
        <Text>Lorem Ipsum Dolor</Text>
        <Text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Enim,
          elementum sed
        </Text>
      </div>
    );
    }
    
    override footer(): JSX.Element | undefined {
    return (
      <Button
        appearance="transparent"
        icon={<ArrowRight16Filled />}
        iconPosition="after"
        size="small"
      >
        View details
      </Button>
    );
    }
    }
    

    ウィジェット内のヘッダー、本文、フッターのコンテンツの例を示すスクリーンショット。

  • ウィジェットをカスタマイズするには、 body()footer() をオーバーライドします。

    export class NewsWidget extends BaseWidget<any, any> {
    override body(): JSX.Element | undefined {
    return (
      <div>
        <Image src="image.svg" />
        <Text>Lorem Ipsum Dolor</Text>
        <Text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Enim,
          elementum sed
        </Text>
      </div>
    );
    }
    
    override footer(): JSX.Element | undefined {
    return (
      <Button
        appearance="transparent"
        icon={<ArrowRight16Filled />}
        iconPosition="after"
        size="small"
      >
        View details
      </Button>
    );
    }
    }
    

    ウィジェットの本文とフッターの内容を示すスクリーンショット。

  • ウィジェットをカスタマイズするには、 body() をオーバーライドします。

    export class NewsWidget extends BaseWidget<any, any> {
    override body(): JSX.Element | undefined {
    return (
      <div>
        <Image src="image.svg" />
        <Text>Lorem Ipsum Dolor</Text>
        <Text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Enim,
          elementum sed
        </Text>
      </div>
    );
    }
    }
    

    ウィジェットの本文の内容を示すスクリーンショット。

データ ローダーを含める

ウィジェットが読み込まれる前にウィジェットにデータ ローダーを含める場合は、ウィジェットの状態にプロパティを追加して、データ ローダーが loading()されていることを示すことができます。 このプロパティを使用すると、読み込みインジケーターをユーザーに表示できます。

例:

    override loading(): JSX.Element | undefined {
     return (
      <div className="loading">
       <Spinner label="Loading..." labelPosition="below" />
      </div>
     );
    }

次に、データの読み込み中に読み込みスピナーが表示されます。 データが読み込まれると、読み込みスピナーが非表示になり、リスト データとフッター ボタンが表示されます。

データの読み込み中に読み込みスピナーがグラフィカルに表示されます。

空の状態を処理する

データが空の場合は、ウィジェットに特定のコンテンツを表示できます。 そのためには、ウィジェット ファイル内の body メソッドを変更して、さまざまな状態のデータを採用する必要があります。

次の例は、 ListWidget のデータが空のときに空のイメージを表示する方法を示しています。

override body(): JSX.Element | undefined {
  let hasData = this.state.data && this.state.data.length > 0;
  return (
    <div>
      {hasData ? (
        <>
          {this.state.data?.map((t: ListModel) => {
            ...
          })}
        </>
      ) : (
        <div>
          <Image src="empty-default.svg" height="150px" />
          <Text align="center">No data</Text>
        </div>
      )}
    </div>
  );
}

同様の方法を使用して、データが空のときにウィジェットのフッター コンテンツを削除できます。

  override footer(): JSX.Element | undefined {
    let hasData = this.state.data && this.state.data.length > 0;
    if (hasData) {
      return <Button>...</Button>;
    }
  }

データが空の場合、リスト ウィジェットは次のように表示されます。

リストにデータがないことを示すスクリーンショット。

スケジュールどおりにデータを更新する

次の例は、ウィジェットにリアルタイム データを表示する方法を示しています。 ウィジェットには、現在の時刻と更新が表示されます。

interface IRefreshWidgetState {
  data: string;
}

export class RefreshWidget extends BaseWidget<any, IRefreshWidgetState> {
  override body(): JSX.Element | undefined {
    return <>{this.state.data}</>;
  }

  async componentDidMount() {
    setInterval(() => {
      this.setState({ data: new Date().toLocaleTimeString() });
    }, 1000);
  }
}

setInterval メソッドを変更して独自の関数を呼び出して、このsetInterval(() => yourGetDataFunction(), 1000)のようなデータを更新できます。

ウィジェットの抽象化

TeamsFx SDK では、ウィジェットの開発を簡略化するために、開発者が継承する BaseWidget クラスを提供し、ウィジェット レイアウトの実装に細心の注意を払わずに、ニーズを満たすウィジェットを実装します。

次のコードは、BaseWidget クラスの例です。

export interface IWidgetClassNames {
  root?: string;
  header?: string;
  body?: string;
  footer?: string;
}

const classNames: IWidgetClassNames = mergeStyleSets({
  root: {
    display: "grid",
    padding: "1.25rem 2rem 1.25rem 2rem",
    backgroundColor: tokens.colorNeutralBackground1,
    border: "1px solid var(--colorTransparentStroke)",
    boxShadow: tokens.shadow4,
    borderRadius: tokens.borderRadiusMedium,
    gap: tokens.spacingHorizontalL,
    gridTemplateRows: "max-content 1fr max-content",
  },
  header: {
    display: "grid",
    height: "max-content",
    "& div": {
      display: "grid",
      gap: tokens.spacingHorizontalS,
      alignItems: "center",
      gridTemplateColumns: "min-content 1fr min-content",
    },
    "& svg": {
      height: "1.5rem",
      width: "1.5rem",
    },
    "& span": {
      fontWeight: tokens.fontWeightSemibold,
      lineHeight: tokens.lineHeightBase200,
      fontSize: tokens.fontSizeBase200,
    },
  },
  footer: {
    "& button": {
      width: "fit-content",
    },
  },
});

interface BaseWidgetState {
  loading?: boolean;
}

export class BaseWidget<P, S> extends Component<P, S & BaseWidgetState> {
  public constructor(props: Readonly<P>) {
    super(props);
    this.state = { loading: undefined } as S & BaseWidgetState;
  }

  public async componentDidMount() {
    this.setState({ ...(await this.getData()), loading: false });
  }

  public render() {
    const { root, header, body, footer } = this.styling();
    const showLoading = this.state.loading !== false && this.loading() !== undefined;
    return (
      <div className={mergeStyles(classNames.root, root)}>
        {this.header() && (
          <div className={mergeStyles(classNames.header, header)}>{this.header()}</div>
        )}
        {showLoading ? (
          this.loading()
        ) : (
          <>
            {this.body() !== undefined && <div className={body}>{this.body()}</div>}
            {this.footer() !== undefined && (
              <div className={mergeStyles(classNames.footer, footer)}>{this.footer()}</div>
            )}
          </>
        )}
      </div>
    );
  }

  protected async getData(): Promise<S> {
    return undefined;
  }

  protected header(): JSX.Element | undefined {
    return undefined;
  }

  protected body(): JSX.Element | undefined {
    return undefined;
  }

  protected footer(): JSX.Element | undefined {
    return undefined;
  }

  protected loading(): JSX.Element | undefined {
    return undefined;
  }

  protected styling(): IWidgetClassNames {
    return {};
  }
}

オーバーライドする推奨される方法を次に示します。

メソッド 機能 オーバーライドすることをお勧めします
constructor() 初期 this.state を呼び出し、スーパー クラス React.Componentのコンストラクターを呼び出します。 いいえ
componentDidMount() コンポーネントがマウントされた後に を呼び出し、getData() メソッドを呼び出して状態のdata プロパティに値を割り当てます。 いいえ
render() 更新がある場合は常にを呼び出します。 ダッシュボードの既定のレイアウトは、このメソッドで定義されています。 いいえ
getData() ウィジェットに必要なデータを呼び出します。 このメソッドによって返される値は、 this.state.dataに設定されます。 はい
header() ウィジェット ヘッダーの外観を呼び出します。 このメソッドをオーバーライドしてウィジェットをカスタマイズするかどうかを選択できます。そうでない場合は、ウィジェットにヘッダーはありません。 はい
body() ウィジェット本体の外観を呼び出します。 このメソッドをオーバーライドしてウィジェットをカスタマイズするかどうかを選択できます。そうでない場合は、ウィジェットに本文はありません。 はい
footer() ウィジェット フッターの外観を呼び出します。 このメソッドをオーバーライドしてウィジェットをカスタマイズするかどうかを選択できます。そうでない場合は、ウィジェットにフッターはありません。 はい
loading() ウィジェットがデータのフェッチ処理中である場合にを呼び出します。 読み込みインジケーターが必要な場合、メソッドは読み込みインジケーターをレンダリングするために必要なコンポーネントを含む JSX.Element を返すことができます。 はい
style() ウィジェットのさまざまな部分のクラス名を定義するオブジェクトを呼び出します。 はい

ウィジェット コンテンツとしての Microsoft Graph Toolkit

Microsoft Graph Toolkit は、一連の更新可能なフレームワークに依存しない Web コンポーネントであり、Microsoft Graph へのアクセスと操作に役立ちます。 Microsoft Graph Toolkit は、任意の Web フレームワークまたはフレームワークなしで使用できます。

ウィジェット コンテンツとして Microsoft Graph Toolkit を使用するには、次の手順に従います。

  1. Teams アプリに SSO 機能を追加する: Microsoft Teamsは、アプリがサインインしている Teams ユーザー トークンを取得して Microsoft Graph にアクセスするためのシングル サインオン (SSO) 機能を提供します。 詳細については、「 Teams アプリの SSO 機能」を参照してください

  2. 必要な npm パッケージをインストールします。

    プロジェクト tabs フォルダーで次のコマンドを実行して、必要な npm パッケージをインストールします。

    npm install @microsoft/mgt-react @microsoft/mgt-teamsfx-provider
    
  3. 新しい Graph Toolkit ウィジェットを追加する: プロジェクト src/views/widgets フォルダーに新しいウィジェット ファイルを作成します (たとえば、 GraphWidget.tsx)。 このウィジェットでは、Microsoft Graph にアクセスするためにアプリに同意するようにユーザーをガイドし、Microsoft Graph Toolkit を使用してユーザーの Todo リストを表示します。

    次のコードは、ウィジェットで Microsoft Graph Toolkit の Todo コンポーネントを使用する例です。

    import { Providers, ProviderState, Todo } from "@microsoft/mgt-react";
    import { TeamsFxProvider } from "@microsoft/mgt-teamsfx-provider";
    
    import { loginAction } from "../../internal/login";
    import { TeamsUserCredentialContext } from "../../internal/singletonContext";
    import { BaseWidget } from "@microsoft/teamsfx-react";
    
    interface IGraphWidgetState {
      needLogin: boolean;
    }
    
    export class GraphWidget extends Widget<any, IGraphWidgetState> {
      override body(): JSX.Element | undefined {
        return <div>{this.state.needLogin === false && <Todo />}</div>;
      }
    
      async componentDidMount() {
        super.componentDidMount();
    
        // Initialize TeamsFx provider
        const provider = new TeamsFxProvider(TeamsUserCredentialContext.getInstance().getCredential(), [
         "Tasks.ReadWrite",
    ]);
        Providers.globalProvider = provider;
    
        // Check if user is signed in
        if (await this.checkIsConsentNeeded()) {
          await loginAction(["Tasks.ReadWrite"]);
    }
    
    // Update signed in state
    Providers.globalProvider.setState(ProviderState.SignedIn);
    this.setState({ needLogin: false });
    
      }
    
      /**
    
      * Check if user needs to consent
      * @returns true if user needs to consent
      */
    
      async checkIsConsentNeeded() {
        let needConsent = false;
        try {
          await TeamsUserCredentialContext.getInstance().getCredential().getToken(["Tasks.ReadWrite"]);
        } catch (error) {
          needConsent = true;
        }
        return needConsent;
      }
    }
    

    ウィジェット内で代替の Microsoft Graph Toolkit コンポーネントを使用できます。 詳細については、「 Microsoft Graph Toolkit」を参照してください。

  4. ダッシュボード レイアウトにウィジェットを追加します。 ダッシュボード ファイルに新しいウィジェットを含めます。

    ...
    export default class YourDashboard extends BaseDashboard<any, any> {
      ...
      override layout(): undefined | JSX.Element {
        return (
          <>
            <GraphWiget />
          </>
        );
      }
      ...
    }
    

次に、Teams アプリを起動または更新すると、Microsoft Graph Toolkit を使用して新しいウィジェットが表示されます。

Graph API 呼び出し

Microsoft Graph API は、Microsoft クラウドやその他のサービスとの通信に使用できる Web API です。 カスタム アプリケーションは、Microsoft Graph API を使用してデータに接続し、それをカスタム アプリケーションで使用して、組織の生産性を向上させることができます。

Graph API 呼び出しロジックを実装する前に、ダッシュボード プロジェクトの SSO を有効にする必要があります。 詳細については、「 Teams アプリにシングル サインオンを追加する」を参照してください。

Graph API 呼び出しを追加するには:

フロントエンドから Graph API を呼び出す (委任されたアクセス許可を使用する)

フロントエンド タブから Graph API を呼び出す場合は、次の手順に従います。

  1. 呼び出す Graph API に関連付けられているアクセス許可スコープの名前を取得するには、「 Graph API」を参照してください。

  2. 呼び出す Graph API に関連するスコープを追加して、Graph クライアントを作成します。

    let credential: TeamsUserCredential;  
    credential = TeamsUserCredentialContext.getInstance().getCredential();
    const graphClient: Client = createMicrosoftGraphClientWithCredential(credential, scope);
    
  3. Graph API を呼び出し、応答を特定のモデルに解析します。

    try {
      const graphApiResult = await graphClient.api("<GRAPH_API_PATH>").get();
      // Parse the graphApiResult into a Model you defined, used by the front-end.
    } catch (e) {}
    

バックエンドから Graph API を呼び出す (アプリケーションのアクセス許可を使用する)

バックエンドから Graph API を呼び出す場合は、次の手順に従います。

  1. 同意アプリケーションのアクセス許可
  2. Azure 関数を追加する
  3. Azure 関数にロジックを追加する
  4. フロントエンドから Azure 関数を呼び出す

アプリケーションのアクセス許可に同意するには、次の手順に従います。

  1. Azure ポータルに移動します。
  2. [ Microsoft Entra ID] を選択します
  3. 左側のウィンドウで [ アプリの登録 ] を選択します。
  4. ダッシュボード アプリを選択します。
  5. 左側のウィンドウで [ API のアクセス許可 ] を選択します。
  6. [アクセス許可の追加] を選択します。
  7. [Microsoft Graph] を選択します。
  8. [アプリケーションのアクセス許可] を選択します。
  9. 必要なアクセス許可を見つけます。
  10. 下部にある [ アクセス許可の追加] ボタンを選択します。
  11. [ 管理者の同意の付与] を選択✔します
  12. [ はい ] ボタンを選択して、管理者の同意を完了します。

Azure 関数を追加する

Visual Studio Code の左側のウィンドウで、Teams Toolkit>[機能の追加]>Azure Functions に移動し、関数名を入力します。

Azure Functions の選択を示すスクリーンショット。

Azure 関数をプロジェクトに追加する方法の詳細については、「 Azure Functions と Teams アプリの統合」を参照してください。

Azure 関数にロジックを追加する

Azure Function という名前のフォルダーの下の index.ts/index.ts で、アプリケーションのアクセス許可を持つバックエンド Graph API 呼び出しを含むロジックを追加できます。 次のコード スニペットを参照してください。

/**
 * This function handles requests from teamsfx client.
 * The HTTP request should contain an SSO token queried from Teams in the header.
 * Before triggering this function, teamsfx binding would process the SSO token and generate teamsfx configuration.
 *
 * You should initializes the teamsfx SDK with the configuration and calls these APIs.
 *
 * The response contains multiple message blocks constructed into a JSON object, including:
 * - An echo of the request body.
 * - The display name encoded in the SSO token.
 * - Current user's Microsoft 365 profile if the user has consented.
 *
 * @param {Context} context - The Azure Functions context object.
 * @param {HttpRequest} req - The HTTP request.
 * @param {teamsfxContext} TeamsfxContext - The context generated by teamsfx binding.
 */
export default async function run(
  context: Context,
  req: HttpRequest,
  teamsfxContext: TeamsfxContext
): Promise<Response> {
  context.log("HTTP trigger function processed a request.");

  // Initialize response.
  const res: Response = {
    status: 200,
    body: {},
  };

  // Your logic here.

  return res;
}

フロントエンドから Azure 関数を呼び出す

関数名で Azure 関数を呼び出します。 Azure 関数を呼び出すには、次のコード スニペットを参照してください。

const functionName = process.env.REACT_APP_FUNC_NAME || "myFunc";
export let taskName: string;

export async function callFunction(params?: string) {
  taskName = params || "";
  const credential = TeamsUserCredentialContext.getInstance().getCredential();
  if (!credential) {
    throw new Error("TeamsFx SDK is not initialized.");
  }
  try {
    const apiBaseUrl = process.env.REACT_APP_FUNC_ENDPOINT + "/api/";    
    const apiClient = createApiClient(
      apiBaseUrl,
      new BearerTokenAuthProvider(async () => (await credential.getToken(""))!.token)
    );
    const response = await apiClient.get(functionName);
    return response.data;
  } catch (err: unknown) {
    ...
  }
}

詳細については、以下を参照してください:

Power BI をダッシュボードに埋め込む

Power BI をダッシュボードに埋め込むには、「 Power BI クライアントの反応」を参照してください。

ステップ バイ ステップのガイド

ステップ バイ ステップ ガイドに従ってダッシュボードを構築し、ダッシュボードにウィジェットと Graph API 呼び出しを追加する方法について説明します。

関連項目