Criar um aplicativo de guia dashboard
Um dashboard é uma ferramenta para rastrear, analisar e exibir dados para obter informações sobre uma organização ou um processo específico. Os painéis no Teams permitem monitorar e exibir métricas importantes.
O modelo de guia dashboard do Teams Toolkit permite que você comece a integrar uma tela com vários cartões que fornecem uma visão geral do conteúdo no Teams. Você pode:
- Use widgets para exibir conteúdo de aplicativos e serviços na guia dashboard.
- Integre seu aplicativo ao API do Graph para visualizar detalhes sobre a implementação dos dados selecionados.
- Crie painéis personalizáveis que permitem que sua empresa defina metas específicas que ajudam você a rastrear as informações necessárias para exibir em várias áreas e em todos os departamentos.
Sua equipe pode obter as atualizações mais recentes de diferentes fontes no Teams usando o aplicativo de guia teams dashboard. Use dashboard aplicativos de guia para conectar várias métricas, fontes de dados, APIs e serviços. Os aplicativos de guia de painel ajudam sua empresa a extrair informações relevantes das fontes e apresentá-los aos usuários. Para obter mais informações sobre como criar um aplicativo de guia dashboard, consulte guia passo a passo.
Adicionar um novo dashboard
Depois de criar um aplicativo de guia dashboard, você pode adicionar um novo dashboard.
Para adicionar um novo dashboard, siga estas etapas:
- Criar uma classe dashboard
- Substituir métodos para personalizar dashboard aplicativo de guia
- Adicionar uma rota para o novo aplicativo de guia dashboard
- Modificar manifesto para adicionar um novo aplicativo de guia dashboard
Criar uma classe dashboard
Crie um arquivo com a .tsx
extensão do dashboard no src/dashboards
diretório, por exemplo, YourDashboard.tsx
. Em seguida, crie uma classe que estende o BaseDashboard class from
@microsoft/teamsfx-react.
//YourDashboard.tsx
import { BaseDashboard } from "@microsoft/teamsfx-react";
export default class YourDashboard extends BaseDashboard<any, any> {}
Observação
Todos os métodos são opcionais. Se você não substituir nenhum método, o layout de dashboard padrão será usado.
Substituir métodos para personalizar dashboard aplicativo de guia
A BaseDashboard
classe fornece poucos métodos que você pode substituir para personalizar o layout dashboard. A tabela a seguir lista os métodos que você pode substituir:
Métodos | Function |
---|---|
styling() |
Personalize o estilo do dashboard. |
layout() |
Definir layout de widgets. |
O código a seguir é um exemplo para personalizar o layout do dashboard:
.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 />
</>
);
}
}
Adicionar uma rota para o novo aplicativo de guia dashboard
Você deve vincular seu widget a um arquivo de fonte de dados. O widget capta os dados apresentados no dashboard do arquivo de origem.
Abra o src/App.tsx
arquivo e adicione uma rota para o novo dashboard. Veja um exemplo:
import YourDashboard from "./dashboards/YourDashboard";
export default function App() {
...
<Route path="/yourdashboard" element={<YourDashboard />} />
...
}
Modificar manifesto para adicionar um novo aplicativo de guia dashboard
Abra o appPackage/manifest.json
arquivo e adicione uma nova guia dashboard em staticTabs
. Para obter mais informações, consulte o manifesto do aplicativo. Veja um exemplo:
{
"entityId": "index1",
"name": "Your Dashboard",
"contentUrl": "${{TAB_ENDPOINT}}/index.html#/yourdashboard",
"websiteUrl": "${{TAB_ENDPOINT}}/index.html#/yourdashboard",
"scopes": ["personal"]
}
Personalizar o layout do dashboard
O TeamsFx fornece métodos convenientes para definir e modificar o layout do dashboard. A seguir estão os métodos:
Três widgets seguidos com a altura de 350 px ocupando 20%, 60% e 20% da largura, respectivamente.
.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 /> </> ); } }
Dois widgets em uma linha com uma largura de 600 px e 1100 px. A altura da primeira linha é a altura máxima de seu conteúdo e a altura da segunda linha é de 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 /> </> ); } }
Organize dois widgets em uma coluna.
.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> </> ); } }
Abstração do aplicativo de guia de painel
Para ajustar o layout do dashboard, o TeamsFx fornece uma BaseDashboard
classe para os desenvolvedores implementarem uma dashboard.
O código a seguir é um exemplo de uma BaseDashboard
classe:
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;
}
}
Na classe, o BaseDashboard
TeamsFx fornece layouts básicos com métodos personalizáveis. O dashboard ainda é um componente react e o TeamsFx fornece implementações básicas de funções com base no ciclo de vida dos componentes react, como:
- Implementando uma lógica de renderização básica com base no layout da grade.
- Adicionar um observador para se adaptar automaticamente a dispositivos móveis.
A seguir estão os métodos personalizáveis para substituir:
Métodos | Função | Recomendamos substituir |
---|---|---|
constructor() |
Inicializa o estado dashboard e as variáveis. | Não |
componentDidMount() |
Invoca depois que um componente é montado. | Não |
componentWillUnmount() |
Invoca quando um componente é desmontado. | Não |
render() |
Invoca quando há uma atualização. O layout padrão dashboard é definido neste método. | Não |
layout |
Define o layout do widget no dashboard. Você pode substituir esse método. | Sim |
styling() |
Para personalizar o estilo do dashboard. Você pode substituir esse método. | Sim |
Use um widget no dashboard
Os widgets exibem informações configuráveis e gráficos nos painéis. Eles aparecem no quadro de widget onde você pode fixar, desapinar, organizar, redimensionar e personalizar widgets para refletir seus interesses. Seu quadro de widget é otimizado para mostrar widgets relevantes e conteúdo personalizado com base no seu uso.
Personalizar o widget
Você pode personalizar o widget substituindo os seguintes métodos na BaseWidget
classe:
Substitua
header()
,body()
efooter()
para personalizar o widget.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> ); } }
Substitua
body()
efooter()
personalize o widget.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> ); } }
Substitua
body()
para personalizar o widget.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> ); } }
Incluir um carregador de dados
Se você quiser incluir um carregador de dados no widget antes que o widget seja carregado, você pode adicionar uma propriedade ao estado do widget para indicar que o carregador de dados é loading()
. Você pode usar essa propriedade para mostrar um indicador de carregamento para o usuário.
Exemplo:
override loading(): JSX.Element | undefined {
return (
<div className="loading">
<Spinner label="Loading..." labelPosition="below" />
</div>
);
}
Agora, o rotador de carregamento é mostrado enquanto os dados são carregados. Quando os dados são carregados, o rotador de carregamento fica oculto e os dados de lista e o botão rodapé são mostrados.
Manipular o estado vazio
Você pode exibir um conteúdo específico no widget quando os dados estiverem vazios. Para fazer isso, você precisa modificar o body
método em seu arquivo widget para adotar diferentes estados dos dados.
O exemplo a seguir mostra como exibir uma imagem vazia quando os dados de ListWidget estão vazios.
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>
);
}
Você pode usar uma abordagem semelhante para remover o conteúdo do rodapé do widget quando os dados estiverem vazios.
override footer(): JSX.Element | undefined {
let hasData = this.state.data && this.state.data.length > 0;
if (hasData) {
return <Button>...</Button>;
}
}
Quando os dados estão vazios, o widget de lista aparece da seguinte maneira:
Atualizar dados conforme agendado
O exemplo a seguir mostra como exibir dados em tempo real em um widget. O widget exibe a hora e as atualizações atuais.
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);
}
}
Você pode modificar setInterval
o método para chamar sua própria função para atualizar dados como este setInterval(() => yourGetDataFunction(), 1000)
.
Abstração do Widget
Para simplificar o desenvolvimento de um widget, o SDK do TeamsFx fornece uma BaseWidget
classe para os desenvolvedores herdarem para implementar um widget que atenda às suas necessidades sem prestar muita atenção para implementar o layout do widget.
O código a seguir é um exemplo da classe 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 {};
}
}
A seguir estão os métodos recomendados para substituir:
Métodos | Função | Recomendamos substituir |
---|---|---|
constructor() |
Invoca a inicial this.state e chama o construtor da super classe React.Component . |
Não |
componentDidMount() |
Invoca depois que um componente é montado e atribui um valor à data propriedade do estado chamando o getData() método. |
Não |
render() |
Invoca sempre que há uma atualização. O layout padrão dashboard é definido neste método. | Não |
getData() |
Invoca os dados necessários pelo widget. O valor retornado por esse método é definido como this.state.data . |
Sim |
header() |
Invoca como é o cabeçalho do widget. Você pode optar por substituir esse método para personalizar um widget ou não, se não, o widget não terá um cabeçalho. | Sim |
body() |
Invoca como é o corpo do widget. Você pode optar por substituir esse método para personalizar um widget ou não, se não, o widget não terá um corpo. | Sim |
footer() |
Invoca como é o rodapé do widget. Você pode optar por substituir esse método para personalizar um widget ou não, se não for, o widget não terá um rodapé. | Sim |
loading() |
Invoca quando o widget está no processo de busca de dados. Se um indicador de carregamento for necessário, o método poderá retornar um JSX.Element que contém os componentes necessários para renderizar o indicador de carregamento. |
Sim |
style() |
Invoca um objeto que define os nomes de classe para as diferentes partes do widget. | Sim |
Microsoft Graph Toolkit como conteúdo de widget
O Microsoft Graph Toolkit é um conjunto de componentes web renováveis e agnósticos da estrutura, que ajuda a acessar e trabalhar com o Microsoft Graph. Você pode usar o Microsoft Graph Toolkit com qualquer estrutura da Web ou sem uma estrutura.
Para usar o Microsoft Graph Toolkit como seu conteúdo de widget, siga estas etapas:
Adicionar o recurso SSO ao seu aplicativo teams: o Microsoft Teams fornece função SSO (logon único) para um aplicativo obter o token de usuário do Teams conectado para acessar o Microsoft Graph. Para obter mais informações, consulte o recurso SSO para seu aplicativo do Teams.
Instale pacotes necessários
npm
.Execute o seguinte comando em sua pasta de projeto
tabs
para instalar os pacotes necessáriosnpm
:npm install @microsoft/mgt-react @microsoft/mgt-teamsfx-provider
Adicionar um novo widget do Graph Toolkit: crie um novo arquivo widget na pasta do projeto
src/views/widgets
, por exemplo,GraphWidget.tsx
. Neste widget, orientaremos os usuários a consentir que nosso aplicativo acesse o Microsoft Graph e mostre a lista Todo do usuário usando o Microsoft Graph Toolkit.O código a seguir é um exemplo de uso do componente Todo do Microsoft Graph Toolkit no widget:
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; } }
Você pode usar componentes alternativos do Microsoft Graph Toolkit em seu widget. Para obter mais informações, confira Kit de Ferramentas do Microsoft Graph.
Adicione o widget ao layout dashboard. Inclua o novo widget no arquivo dashboard.
... export default class YourDashboard extends BaseDashboard<any, any> { ... override layout(): undefined | JSX.Element { return ( <> <GraphWiget /> </> ); } ... }
Agora, inicie ou atualize seu aplicativo teams, você verá o novo widget usando o Microsoft Graph Toolkit.
API do Graph chamada
O Microsoft API do Graph é uma API Web que você pode usar para se comunicar com a nuvem da Microsoft e outros serviços. Aplicativos personalizados podem usar a API do Microsoft Graph para se conectar aos dados e usá-los em aplicativos personalizados para aprimorar a produtividade organizacional.
Antes de implementar sua lógica de chamada API do Graph, é necessário habilitar o SSO para seu projeto de dashboard. Para obter mais informações, confira Adicionar logon único ao aplicativo do Teams.
Para adicionar uma chamada API do Graph:
- Chamar API do Graph do front-end (usar permissões delegadas)
- Chamar API do Graph do back-end (usar permissões de aplicativo)
Chamar API do Graph do front-end (usar permissões delegadas)
Se você quiser chamar um API do Graph na guia front-end, siga estas etapas:
Para obter o nome do escopo de permissão associado ao API do Graph você pretende invocar, consulte API do Graph.
Crie um cliente graph adicionando o escopo relacionado ao API do Graph que você deseja chamar.
let credential: TeamsUserCredential; credential = TeamsUserCredentialContext.getInstance().getCredential(); const graphClient: Client = createMicrosoftGraphClientWithCredential(credential, scope);
Chame o API do Graph e analise a resposta em um determinado modelo.
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) {}
Chamar API do Graph do back-end (usar permissões de aplicativo)
Se você quiser chamar um API do Graph do back-end, siga estas etapas:
- Permissões de aplicativo de consentimento
- Adicionar uma função do Azure
- Adicionar sua lógica na função do Azure
- Chame a função do Azure do front-end
Permissões de aplicativo de consentimento
Para consentir permissões de aplicativo, siga estas etapas:
- Acesse o portal do Azure.
- Selecione Azure Active Directory.
- Selecione Registros de aplicativo no painel esquerdo.
- Selecione seu aplicativo dashboard.
- Selecione permissões de API no painel esquerdo.
- Selecione Adicionar permissão.
- Selecione Microsoft Graph.
- Selecione Permissões de aplicativos.
- Encontre as permissões necessárias.
- Selecione o botão Adicionar permissões na parte inferior.
- Selecione ✔Conceder consentimento do administrador.
- Selecione o botão Sim para concluir o consentimento do administrador.
Adicionar uma função do Azure
No painel esquerdo do Visual Studio Code, vá para o Teams Toolkit>Adicionando recursos>Azure Functions e insira o nome da função.
Para obter mais informações sobre como adicionar uma Função do Azure ao seu projeto, confira integrar Azure Functions ao aplicativo teams.
Adicionar sua lógica na função do Azure
index.ts
/index.ts
Na pasta chamada Função do Azure, você pode adicionar sua lógica que contém chamadas de back-end API do Graph com permissões de aplicativo. Consulte o seguinte snippet de código:
/**
* 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;
}
Chame a função do Azure do front-end
Chame a função do Azure pelo nome da função. Consulte o seguinte snippet de código para chamar a função do 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) {
...
}
}
Para saber mais, veja:
Insira o Power BI para dashboard
Para inserir o Power BI no dashboard, consulte o cliente do Power BI reagir.
Guias passo a passo
Siga o guia passo a passo para criar um dashboard e aprenda a adicionar um widget e API do Graph chamada ao dashboard.