(r, n) => {
        order += '3'
        return n(r)
      }


async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '0'
        return Promise.resolve({ status: 200, success: true })
      })
      .use((r, n) => {
        order += '1'
        return n(r)
      })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('10')
  }


() => {
        order += '0'
        return Promise.resolve({ status: 200, success: true })
      }


(r, n) => {
        order += '1'
        return n(r)
      }


async () => {
    // Arrange
    const middleware = jest.fn(() =>
      Promise.resolve({ status: 200, success: true })
    )
    const sut = new HttpClient()
      .use(middleware)
      .use((_, n) => n({ changed: true }))

    // Act
    await sut.send({})

    // Assert
    expect(middleware).toHaveBeenCalledWith(
      { changed: true },
      expect.any(Function)
    )
  }


() =>
      Promise.resolve({ status: 200, success: true })


(_, n) => n({ changed: true })


async () => {
    // Arrange
    const middleware = jest.fn()
    const sut = new HttpClient()
      .use(middleware)
      .use(() => Promise.resolve({ status: 200, success: true }))

    // Act
    await sut.send({})

    // Assert
    expect(middleware).not.toHaveBeenCalled()
  }


() => Promise.resolve({ status: 200, success: true })


async () => {
    // Arrange
    const middleware = jest.fn(() =>
      Promise.resolve({ status: 200, success: true })
    )

    const sut = new HttpClient().use((_, n) => n()).use(middleware)

    // Act
    await sut.send({ changed: false })

    // Assert
    expect(middleware).toHaveBeenCalledWith(
      { changed: false },
      expect.any(Function)
    )
  }


(_, n) => n()


() => {
  it('Returns body of result', async () => {
    // Arrange
    const sut = new HttpClient().use(() => {
      return Promise.resolve({ status: 200, success: true, body: 'body' })
    })

    // Act
    const response = await sut.$send({ GET: 'foo' })

    // Assert
    expect(response).toBe('body')
  })

  test('Requires validation', async () => {
    // Arrange
    const sut = new HttpClient().use(() =>
      Promise.resolve({
        success: false,
        status: 200,
        message: 'OK',
        body: true,
      })
    )

    // Act
    const act = sut.$send({ method: 'DELETE' })

    // Assert
    await expect(act).rejects.toThrow(
      new Error(
        'Response does not indicate success\n\n' +
          'Request: {\n  "method": "DELETE"\n}\n\n' +
          'Response: {\n  "status": 200,\n  "message": "OK",\n  "body": true\n}'
      )
    )
  })
}


async () => {
    // Arrange
    const sut = new HttpClient().use(() => {
      return Promise.resolve({ status: 200, success: true, body: 'body' })
    })

    // Act
    const response = await sut.$send({ GET: 'foo' })

    // Assert
    expect(response).toBe('body')
  }


() => {
      return Promise.resolve({ status: 200, success: true, body: 'body' })
    }


async () => {
    // Arrange
    const sut = new HttpClient().use(() =>
      Promise.resolve({
        success: false,
        status: 200,
        message: 'OK',
        body: true,
      })
    )

    // Act
    const act = sut.$send({ method: 'DELETE' })

    // Assert
    await expect(act).rejects.toThrow(
      new Error(
        'Response does not indicate success\n\n' +
          'Request: {\n  "method": "DELETE"\n}\n\n' +
          'Response: {\n  "status": 200,\n  "message": "OK",\n  "body": true\n}'
      )
    )
  }


() =>
      Promise.resolve({
        success: false,
        status: 200,
        message: 'OK',
        body: true,
      })


() => {
  it('Adds middleware to be called in reverse order', async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(function a() {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .use(function b(r, n) {
        order += '2'
        return n(r)
      })
      .use(function c(r, n) {
        order += '3'
        return n(r)
      })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('321')
  })
}


async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(function a() {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .use(function b(r, n) {
        order += '2'
        return n(r)
      })
      .use(function c(r, n) {
        order += '3'
        return n(r)
      })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('321')
  }


() => {
  it('Adds middleware to run before all others', async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .use((r, n) => {
        order += '2'
        return n(r)
      })
      .use((r, n) => {
        order += '3'
        return n(r)
      })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('321')
  })

  it('Adds middleware to run after all others but before terminating middleware', async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .use((r, n) => {
        order += '2'
        return n(r)
      })
      .useAt(-1, (r, n) => {
        order += '3'
        return n(r)
      })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('231')
  })

  it('Adds only middleware', async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient().useAt(-1, () => {
      order += '1'
      return Promise.resolve({ status: 200, success: true })
    })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('1')
  })
}


async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .use((r, n) => {
        order += '2'
        return n(r)
      })
      .useAt(-1, (r, n) => {
        order += '3'
        return n(r)
      })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('231')
  }


async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient().useAt(-1, () => {
      order += '1'
      return Promise.resolve({ status: 200, success: true })
    })

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('1')
  }


() => {
      order += '1'
      return Promise.resolve({ status: 200, success: true })
    }


() => {
  it('Registers middleware when true', async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .if(true, (c) =>
        c.use((r, n) => {
          order += '2'
          return n(r)
        })
      )

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('21')
  })

  it('Does not register middleware when false', async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .if(false, (c) =>
        c.use((r, n) => {
          order += '2'
          return n(r)
        })
      )

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('1')
  })
}


async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .if(true, (c) =>
        c.use((r, n) => {
          order += '2'
          return n(r)
        })
      )

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('21')
  }


(c) =>
        c.use((r, n) => {
          order += '2'
          return n(r)
        })


(r, n) => {
          order += '2'
          return n(r)
        }


async () => {
    // Arrange
    let order = ''
    const sut = new HttpClient()
      .use(() => {
        order += '1'
        return Promise.resolve({ status: 200, success: true })
      })
      .if(false, (c) =>
        c.use((r, n) => {
          order += '2'
          return n(r)
        })
      )

    // Act
    await sut.send({})

    // Assert
    expect(order).toEqual('1')
  }


({ className }) => (

About

    <AboutMDX />


({ path }: { path: string }): JSX.Element => (
  <>
    <SEO title='about'


(data: CharacterStore[]) => {
      return data.map((data) => {
        const character = new CharacterStore();

        for (const key of Object.keys(data)) {
          // @ts-ignore
          character[key] = data[key];
        }

        const inventory = new InventoryStore(character, {
          unit: data.inventory.unit,
          coins: data.inventory.coins,
        });
        const attributes = new CharacterAttributesStore(character);
        const skills = new CharacterSkillsStore(character);
        const notes = new CharacterNotesStore();

        attributes.values = data.attributes.values.map(
          (attribute) => new CharacterAttribute(attribute, character)
        );
        skills.values = data.skills.values.map(
          (skill) => new CharacterSkill(skill, character)
        );
        inventory.items = data.inventory.items.map(
          (item) => new InventoryItem(item)
        );
        notes.values = data.notes.values.map(
          (note) => new CharacterNote(note.text, note.date, note.id)
        );

        character.inventory = inventory;
        character.attributes = attributes;
        character.skills = skills;
        character.notes = notes;

        return character;
      });
    }


(data) => {
        const character = new CharacterStore();

        for (const key of Object.keys(data)) {
          // @ts-ignore
          character[key] = data[key];
        }

        const inventory = new InventoryStore(character, {
          unit: data.inventory.unit,
          coins: data.inventory.coins,
        });
        const attributes = new CharacterAttributesStore(character);
        const skills = new CharacterSkillsStore(character);
        const notes = new CharacterNotesStore();

        attributes.values = data.attributes.values.map(
          (attribute) => new CharacterAttribute(attribute, character)
        );
        skills.values = data.skills.values.map(
          (skill) => new CharacterSkill(skill, character)
        );
        inventory.items = data.inventory.items.map(
          (item) => new InventoryItem(item)
        );
        notes.values = data.notes.values.map(
          (note) => new CharacterNote(note.text, note.date, note.id)
        );

        character.inventory = inventory;
        character.attributes = attributes;
        character.skills = skills;
        character.notes = notes;

        return character;
      }


(attribute) => new CharacterAttribute(attribute, character)


(skill) => new CharacterSkill(skill, character)


(item) => new InventoryItem(item)


(note) => new CharacterNote(note.text, note.date, note.id)


(characters: CharacterStore[]) => {
      return characters;
    }


(c) => c.id !== id


(c) => c.id === id


export class RootStore {
  @format<any, any>(
    (data: CharacterStore[]) => {
      return data.map((data) => {
        const character = new CharacterStore();

        for (const key of Object.keys(data)) {
          // @ts-ignore
          character[key] = data[key];
        }

        const inventory = new InventoryStore(character, {
          unit: data.inventory.unit,
          coins: data.inventory.coins,
        });
        const attributes = new CharacterAttributesStore(character);
        const skills = new CharacterSkillsStore(character);
        const notes = new CharacterNotesStore();

        attributes.values = data.attributes.values.map(
          (attribute) => new CharacterAttribute(attribute, character)
        );
        skills.values = data.skills.values.map(
          (skill) => new CharacterSkill(skill, character)
        );
        inventory.items = data.inventory.items.map(
          (item) => new InventoryItem(item)
        );
        notes.values = data.notes.values.map(
          (note) => new CharacterNote(note.text, note.date, note.id)
        );

        character.inventory = inventory;
        character.attributes = attributes;
        character.skills = skills;
        character.notes = notes;

        return character;
      });
    },
    (characters: CharacterStore[]) => {
      return characters;
    }
  )
  characters: CharacterStore[] = [];

  @ignore
  selectedCharacterId: string = '';

  selectCharacter(id: string) {
    if (this.characterById(id)) {
      this.selectedCharacterId = id;
    }
  }

  @ignore
  get currentCharacter(): CharacterStore | null {
    return this.characterById(this.selectedCharacterId) || null;
  }

  language: string = 'de';

  constructor() {
    makeAutoObservable(this);
  }

  createCharacter(name: string) {
    const character = new CharacterStore(name);

    this.characters.push(character);

    return character;
  }

  removeCharacter(id: string) {
    this.characters = this.characters.filter((c) => c.id !== id);
  }

  characterById(id: string) {
    return this.characters.find((c) => c.id === id);
  }

  setLanguage(lang: string) {
    this.language = lang;
    i18n.changeLanguage(lang);
  }
}


selectCharacter(id: string) {
    if (this.characterById(id)) {
      this.selectedCharacterId = id;
    }
  }


createCharacter(name: string) {
    const character = new CharacterStore(name);

    this.characters.push(character);

    return character;
  }


removeCharacter(id: string) {
    this.characters = this.characters.filter((c) => c.id !== id);
  }


characterById(id: string) {
    return this.characters.find((c) => c.id === id);
  }


setLanguage(lang: string) {
    this.language = lang;
    i18n.changeLanguage(lang);
  }


function alignToGrid(boxes: Box[], viewportWidth: number) {
  if (viewportWidth === 0) {
    return;
  }
  // Make Dagre's layout more "grid compatible" by centering each row and
  // wrapping rows so they don't scroll offscreen
  const splitWidth = viewportWidth - 40;
  const itemsPerRow = Math.round(splitWidth / 280);
  const itemWidth = (splitWidth - (itemsPerRow - 1) * PADDING) / itemsPerRow;

  const rows: {top: number; items: Box[]}[] = [];
  for (const box of boxes) {
    if (rows[rows.length - 1]?.top === box.layout.top) {
      rows[rows.length - 1].items.push(box);
    } else {
      rows.push({items: [box], top: box.layout.top});
    }
  }

  let x = -1;
  let y = INSET;

  for (const {items} of rows) {
    const centeringOffset =
      items.length < itemsPerRow ? (splitWidth - items.length * (itemWidth + PADDING)) / 2 : 0;

    for (const box of items) {
      x++;
      if (x >= itemsPerRow) {
        y += box.layout.height + PADDING;
        x = 0;
      }
      box.layout.width = itemWidth;
      box.layout.left = INSET + centeringOffset + x * (itemWidth + PADDING);
      box.layout.top = y;
    }
    y += items[0].layout.height + PADDING;
    x = -1;
  }
}


function flattenToGrid(boxes: Box[], viewportWidth: number) {
  if (viewportWidth === 0) {
    return;
  }
  // Try to arrange the bundles so the downstream ones are generally lower in the grid.
  // Algorithm: Iterate through the folders and insert each into the result set such that
  // it is before things that depend on it and after things that it depends on.
  // If we can't satisfy these (bi-directional deps are possible), we put at the midpoint.

  const splitWidth = viewportWidth - 40;
  const itemsPerRow = Math.round(splitWidth / 280);
  const itemWidth = (splitWidth - (itemsPerRow - 1) * PADDING) / itemsPerRow;

  let x = -1;
  let y = INSET;
  const centeringOffset =
    boxes.length < itemsPerRow ? (splitWidth - boxes.length * (itemWidth + PADDING)) / 2 : 0;

  boxes.forEach((box) => {
    x++;
    if (x >= itemsPerRow) {
      x = 0;
      y += box.layout.height + PADDING;
    }
    box.layout.top = y;
    box.layout.left = INSET + centeringOffset + x * (itemWidth + PADDING);
    box.layout.width = itemWidth;
  });
}


function expandBoxes(layout: Layout, expanded: string | null, viewportWidth: number) {
  // Find the box we want to expand
  const boxIdx = layout.boxes.findIndex((b) => b.id === expanded);
  if (boxIdx === -1) {
    return {boxes: layout.boxes, shadows: []};
  }

  const boxes: Box[] = JSON.parse(JSON.stringify(layout.boxes));
  const box = boxes[boxIdx]!;
  const shadow = {
    top: box.layout.top + box.layout.height + PADDING / 2,
    height: 0,
  };
  const toPushDown = boxes.filter((b) => b.layout.top >= shadow.top);

  const contentsWithChildren: {[id: string]: string[]} = {};
  box.contentIds.forEach((c) => (contentsWithChildren[c] = []));

  const contentsEdges = uniqBy(
    layout.edges
      .filter((e) => contentsWithChildren[e.from] && contentsWithChildren[e.to])
      .map((e) => ({
        from: contentsWithChildren[e.from] ? e.from : layout.bundleForAssetId[e.from],
        to: contentsWithChildren[e.to] ? e.to : layout.bundleForAssetId[e.to],
      })),
    JSON.stringify,
  );
  const contents =
    contentsEdges.length > 0 && box.contentIds.length < 100
      ? runMinimalDagreLayout(contentsWithChildren, contentsEdges)
      : {
          boxes: Object.entries(contentsWithChildren).map(([bundleId, contentIds]) => ({
            id: bundleId,
            contentIds,
            layout: {top: 0, left: 0, width: 250, height: 108},
          })),
        };

  if (contentsEdges.length === 0 || contents.boxes.length > 10) {
    flattenToGrid(contents.boxes, viewportWidth);
  } else {
    alignToGrid(contents.boxes, viewportWidth);
  }

  // Add the nodes for the childern
  boxes.splice(boxIdx, 0, ...contents.boxes);
  for (const box of contents.boxes) {
    box.layout.top += shadow.top;
  }

  // Push the nodes beneath the shadow down
  const contentBottom = Math.max(...contents.boxes.map((b) => b.layout.top + b.layout.height));
  shadow.height = contentBottom - shadow.top + PADDING / 2;
  toPushDown.forEach((box) => (box.layout.top += shadow.height));

  return {boxes, shadows: [shadow]};
}


function useAssetGridLayout(assetGraphData: GraphData | null, viewportWidth: number) {
  return React.useMemo(() => {
    if (!assetGraphData) {
      return {bundles: {}, bundleForAssetId: {}, boxes: [], edges: []};
    }

    const assetIds = Object.keys(assetGraphData.nodes);
    const bundles = identifyBundles(assetIds);
    const bundleForAssetId: {[assetId: string]: string} = {};
    for (const [bundleId, childrenIds] of Object.entries(bundles)) {
      childrenIds.forEach((c) => (bundleForAssetId[c] = bundleId));
    }

    const unbundledAssetIds = assetIds.filter((id) => !bundleForAssetId[id]);
    if (unbundledAssetIds.length) {
      bundles[NONE_BUNDLE] = unbundledAssetIds;
      bundles[NONE_BUNDLE].forEach((id) => (bundleForAssetId[id] = NONE_BUNDLE));
    }

    const edges = flatMap(Object.entries(assetGraphData.downstream), ([from, downstreams]) =>
      Object.keys(downstreams).map((to) => ({from, to})),
    );

    const {boxes} = runMinimalDagreLayout(
      bundles,
      uniqBy(
        edges.map((e) => ({from: bundleForAssetId[e.from], to: bundleForAssetId[e.to]})),
        JSON.stringify,
      ),
    );
    if (boxes.length > 10) {
      flattenToGrid(boxes, viewportWidth);
    } else {
      alignToGrid(boxes, viewportWidth);
    }

    return {bundles, bundleForAssetId, boxes, edges};
  }, [assetGraphData, viewportWidth]);
}


() => {
  const params = useParams();
  const explorerPath = instanceAssetsExplorerPathFromString(params[0]);
  const {assetGraphData} = useAssetGraphData(null, explorerPath.opsQuery || '*');

  return (
    <Box
      flex={{direction: 'column', justifyContent: 'stretch'}}


(
  bundles: {[prefixId: string]: string[]},
  edges: {from: string; to: string}[],
) => {
  const g = new dagre.graphlib.Graph({compound: true});

  g.setGraph({
    rankdir: 'TB',
    marginx: 0,
    marginy: 0,
    nodesep: 20,
    edgesep: 10,
    ranksep: 20,
  });
  g.setDefaultEdgeLabel(() => ({}));
  for (const [node, contentIds] of Object.entries(bundles)) {
    g.setNode(node, {width: 250, height: contentIds.length ? 40 : 108});
  }
  for (const edge of edges) {
    g.setEdge({v: edge.from, w: edge.to}, {weight: 1});
  }

  dagre.layout(g);

  const boxes = Object.entries(bundles).map(([bundleId, contentIds]) => {
    const {x, y, width, height} = g.node(bundleId);
    return {id: bundleId, contentIds, layout: {top: y, left: x, width, height}};
  });

  // Note: This is important for other algorithms we run later, but we want to
  // do it in the layer that is cached.
  boxes.sort((a, b) => a.id.localeCompare(b.id));

  return {boxes};
}


([bundleId, contentIds]) => {
    const {x, y, width, height} = g.node(bundleId);
    return {id: bundleId, contentIds, layout: {top: y, left: x, width, height}};
  }


(a, b) => a.id.localeCompare(b.id)


(box) => {
    x++;
    if (x >= itemsPerRow) {
      x = 0;
      y += box.layout.height + PADDING;
    }
    box.layout.top = y;
    box.layout.left = INSET + centeringOffset + x * (itemWidth + PADDING);
    box.layout.width = itemWidth;
  }


(b) => b.id === expanded


(b) => b.layout.top >= shadow.top


(c) => (contentsWithChildren[c] = [])


(e) => contentsWithChildren[e.from] && contentsWithChildren[e.to]


(e) => ({
        from: contentsWithChildren[e.from] ? e.from : layout.bundleForAssetId[e.from],
        to: contentsWithChildren[e.to] ? e.to : layout.bundleForAssetId[e.to],
      })


([bundleId, contentIds]) => ({
            id: bundleId,
            contentIds,
            layout: {top: 0, left: 0, width: 250, height: 108},
          })


(b) => b.layout.top + b.layout.height


(box) => (box.layout.top += shadow.height)


() => {
    if (!assetGraphData) {
      return {bundles: {}, bundleForAssetId: {}, boxes: [], edges: []};
    }

    const assetIds = Object.keys(assetGraphData.nodes);
    const bundles = identifyBundles(assetIds);
    const bundleForAssetId: {[assetId: string]: string} = {};
    for (const [bundleId, childrenIds] of Object.entries(bundles)) {
      childrenIds.forEach((c) => (bundleForAssetId[c] = bundleId));
    }

    const unbundledAssetIds = assetIds.filter((id) => !bundleForAssetId[id]);
    if (unbundledAssetIds.length) {
      bundles[NONE_BUNDLE] = unbundledAssetIds;
      bundles[NONE_BUNDLE].forEach((id) => (bundleForAssetId[id] = NONE_BUNDLE));
    }

    const edges = flatMap(Object.entries(assetGraphData.downstream), ([from, downstreams]) =>
      Object.keys(downstreams).map((to) => ({from, to})),
    );

    const {boxes} = runMinimalDagreLayout(
      bundles,
      uniqBy(
        edges.map((e) => ({from: bundleForAssetId[e.from], to: bundleForAssetId[e.to]})),
        JSON.stringify,
      ),
    );
    if (boxes.length > 10) {
      flattenToGrid(boxes, viewportWidth);
    } else {
      alignToGrid(boxes, viewportWidth);
    }

    return {bundles, bundleForAssetId, boxes, edges};
  }


(c) => (bundleForAssetId[c] = bundleId)


(id) => !bundleForAssetId[id]


(id) => (bundleForAssetId[id] = NONE_BUNDLE)


([from, downstreams]) =>
      Object.keys(downstreams).map((to) => ({from, to}))


(to) => ({from, to})


(e) => ({from: bundleForAssetId[e.from], to: bundleForAssetId[e.to]})


({assetGraphData}) => {
  const [highlighted, setHighlighted] = React.useState<string | null>(null);
  const [expanded, setExpanded] = React.useState<string | null>(null);
  const {viewport, containerProps} = useViewport();
  const layout = useAssetGridLayout(assetGraphData, viewport.width);
  const {edges, bundleForAssetId} = layout;
  const {boxes, shadows} = React.useMemo(() => expandBoxes(layout, expanded, viewport.width), [
    layout,
    expanded,
    viewport.width,
  ]);

  const expandedAssetKeys =
    boxes.find((b) => b.id === expanded)?.contentIds.map((id) => ({path: JSON.parse(id)})) || [];

  const {liveResult, liveDataByNode} = useLiveDataForAssetKeys(
    null,
    assetGraphData,
    expandedAssetKeys,
  );

  useQueryRefreshAtInterval(liveResult, FIFTEEN_SECONDS);

  if (!assetGraphData) {
    return <LoadingSpinner purpose="page" />;
  }

  const renderedIds = keyBy(
    boxes.filter((b) => expanded !== b.id),
    (b) => b.id,
  );
  const renderedEdges = uniqBy(
    edges.map((e) => ({
      from: renderedIds[e.from] ? e.from : bundleForAssetId[e.from] || e.from,
      to: renderedIds[e.to] ? e.to : bundleForAssetId[e.to] || e.to,
    })),
    JSON.stringify,
  );

  const bottom = Math.max(
    ...shadows.map((s) => s.top + s.height),
    ...boxes.map((b) => b.layout.top + b.layout.height),
  );

  return (
    <div
      {...containerProps}
      style={{
        overflowY: 'scroll',
        position: 'relative',
        width: '100%',
        height: bottom,
      }}


() => expandBoxes(layout, expanded, viewport.width)


(id) => ({path: JSON.parse(id)})


(b) => expanded !== b.id


(b) => b.id


(e) => ({
      from: renderedIds[e.from] ? e.from : bundleForAssetId[e.from] || e.from,
      to: renderedIds[e.to] ? e.to : bundleForAssetId[e.to] || e.to,
    })


(s) => s.top + s.height


(shadow) => (
        <div
          key={shadow.top}


(b) => !b.contentIds.length


(box) => (
            <div
              key={box.id}


(b) => b.contentIds.length


(box) => (
            <AssetGridItem
              key={box.id}


({
  boxes,
  edges,
  highlighted,
  expanded,
}: {
  boxes: Box[];
  highlighted: string | null;
  expanded: string | null;
  edges: {from: string; to: string}[];
}) => {
  const highlightedEdges =
    highlighted && highlighted !== expanded
      ? edges.filter((e) => e.from === highlighted || e.to === highlighted)
      : [];

  const expandedChildren = boxes.find((b) => b.id === expanded)?.contentIds || [];
  const expandedEdges = edges.filter(
    (e) => expandedChildren.includes(e.from) || expandedChildren.includes(e.to),
  );

  const showBaseEdgeHintsAlways = expandedEdges.length === 0; // && boxes.length < 30
  const showExpandedEdgeHintsAlways = expandedEdges.length > 0;

  const width = Math.max(...boxes.map((b) => b.layout.width + b.layout.left));
  const height = Math.max(...boxes.map((b) => b.layout.height + b.layout.top));

  return (
    <React.Fragment>
      {showBaseEdgeHintsAlways && (
        <StyledPathSet
          key="base"
          boxes={boxes}
          edges={edges}
          color="rgba(35, 31, 27, 0.06)"
          zIndex={0}
          width={width}
          height={height}
        />


(e) => e.from === highlighted || e.to === highlighted


(e) => expandedChildren.includes(e.from) || expandedChildren.includes(e.to)


(b) => b.layout.width + b.layout.left


(b) => b.layout.height + b.layout.top


(e) => e.to === highlighted


(e) => e.from === highlighted


({boxes, edges, color, zIndex, width, height}) => {
  const pointForAssetId = (bundleId: string) => {
    const box = boxes.find((b) => b.id === bundleId);
    if (!box) {
      console.log(bundleId);
      return {x: 0, y: 0};
    }
    const {width, height, left, top} = box.layout;
    return {x: left + width / 2, y: top + height / 2};
  };
  const stackings = {left: 0, right: 0};

  const dForEdge = (edge: {from: string; to: string}) => {
    const source = pointForAssetId(edge.from);
    source.x += boxes[0].layout.width / 2 - 24;
    const target = pointForAssetId(edge.to);
    target.x -= boxes[0].layout.width / 2 - 24;

    if (source.y < target.y) {
      target.y -= 30;
    } else if (source.y > target.y) {
      target.y += 30; //boxes[0].layout.height / 2 - stackings.bottom++ * 10;
    } else {
      if (source.x < target.x) {
        target.x -= 30;
        source.y += stackings.left;
        target.y += stackings.left;
        stackings.left += 7;
      } else if (source.x > target.x) {
        target.x += 30;
        source.y += stackings.right;
        target.y += stackings.right;
        stackings.right += 7;
      }
    }
    return buildSVGPath({source, target});
  };

  const markerId = React.useRef(`arrow${Math.random()}`);

  return (
    <svg style={{zIndex, width, height, position: 'absolute', pointerEvents: 'none'}}


(bundleId: string) => {
    const box = boxes.find((b) => b.id === bundleId);
    if (!box) {
      console.log(bundleId);
      return {x: 0, y: 0};
    }
    const {width, height, left, top} = box.layout;
    return {x: left + width / 2, y: top + height / 2};
  }


(b) => b.id === bundleId


(edge: {from: string; to: string}) => {
    const source = pointForAssetId(edge.from);
    source.x += boxes[0].layout.width / 2 - 24;
    const target = pointForAssetId(edge.to);
    target.x -= boxes[0].layout.width / 2 - 24;

    if (source.y < target.y) {
      target.y -= 30;
    } else if (source.y > target.y) {
      target.y += 30; //boxes[0].layout.height / 2 - stackings.bottom++ * 10;
    } else {
      if (source.x < target.x) {
        target.x -= 30;
        source.y += stackings.left;
        target.y += stackings.left;
        stackings.left += 7;
      } else if (source.x > target.x) {
        target.x += 30;
        source.y += stackings.right;
        target.y += stackings.right;
        stackings.right += 7;
      }
    }
    return buildSVGPath({source, target});
  }


(edge, idx) => (
        <StyledPath
          key={idx}


({
  box,
  expanded,
  highlighted,
  setHighlighted,
  toggleExpanded,
}: {
  box: Box;
  highlighted: boolean;
  setHighlighted: (s: string | null) => void;
  expanded: boolean;
  toggleExpanded: () => void;
}) => {
  return (
    <AssetNodeContainer
      $selected={false}
      $padded={false}
      style={{
        margin: 0,
        position: 'absolute',
        background: 'transparent',
        outline: expanded ? `10px solid #FDF6EF` : 'none',
        boxShadow: expanded ? `0px 15px 0 9px #FDF6EF` : 'none',


interface Box {
  id: string;
  contentIds: string[];
  layout: {top: number; left: number; width: number; height: number};
}


type Layout = ReturnType<typeof useAssetGridLayout>;


dForEdge(edge)


displayNameForAssetKey({path: JSON


instanceAssetsExplorerPathToURL({
                opsQuery: `${tokenForAssetKey


props => {
    const { size, withHoverEffect, color, margin, ...restProps } = props;
    return (
        <SvgIcon {...{ size, withHoverEffect, color, margin, ...restProps }}>
            <SideEffect1546LineIconSvg {...restProps} width="1em" height="1em" />
        </SvgIcon>


() => getFakeJobRole({ post })


export class seedJobs1632821322960 implements MigrationInterface {
    name = 'seedJobs1632821322960'

    public async up(queryRunner: QueryRunner): Promise<void> {
      const connection = getConnection()
      for (let index = 0; index < 3000000; index++) {

        const HR = await connection.getRepository(Users)
            .createQueryBuilder("users")
            .where("role_id = 'HIRING_MANAGER'")
            .andWhere("random() < 0.01")
            .getOne()
        const post = getFakeJobPost();
        post.user_id = HR.user_id
        const roles: JobRole[] =  [...Array(Math.floor(Math.random() * 10 + 1))].map(() => getFakeJobRole({ post }))
        // console.log(roles)
        await connection.getRepository(JobRole).save(roles)
        await connection.getRepository(JobPost).save(post)
        console.log(`Inserted ${roles.length} jobRoles for ${index+1} jobPost`)
      }
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
    }

}


public async up(queryRunner: QueryRunner): Promise<void> {
      const connection = getConnection()
      for (let index = 0; index < 3000000; index++) {

        const HR = await connection.getRepository(Users)
            .createQueryBuilder("users")
            .where("role_id = 'HIRING_MANAGER'")
            .andWhere("random() < 0.01")
            .getOne()
        const post = getFakeJobPost();
        post.user_id = HR.user_id
        const roles: JobRole[] =  [...Array(Math.floor(Math.random() * 10 + 1))].map(() => getFakeJobRole({ post }))
        // console.log(roles)
        await connection.getRepository(JobRole).save(roles)
        await connection.getRepository(JobPost).save(post)
        console.log(`Inserted ${roles.length} jobRoles for ${index+1} jobPost`)
      }
    }


public async down(queryRunner: QueryRunner): Promise<void> {
    }


export type FlatVariant = typeof FlatVariants[keyof typeof FlatVariants];


export type OutlineVariant = typeof OutlineVariants[keyof typeof OutlineVariants];


export type ColorVariant = FlatVariant | OutlineVariant;


(zadano:


() => {
  const tree = TestRenderer.create(<Rival/>).toJSON()
  expect(tree).toMatchSnapshot()
}


export interface MutableDashboard {
  title: string;
  meta: DashboardMeta;
  destroy: () => void;
}


export interface DashboardDTO {
  redirectUri?: string;
  dashboard: DashboardDataDTO;
  meta: DashboardMeta;
}


export interface DashboardMeta {
  canSave?: boolean;
  canEdit?: boolean;
  canDelete?: boolean;
  canShare?: boolean;
  canStar?: boolean;
  canAdmin?: boolean;
  url?: string;
  folderId?: number;
  fullscreen?: boolean;
  fromExplore?: boolean;
  isEditing?: boolean;
  canMakeEditable?: boolean;
  submenuEnabled?: boolean;
  provisioned?: boolean;
  provisionedExternalId?: string;
  focusPanelId?: number;
  isStarred?: boolean;
  showSettings?: boolean;
  expires?: string;
  isSnapshot?: boolean;
  folderTitle?: string;
  folderUrl?: string;
  created?: string;
  isDynamicViewEnabled?: boolean;
}


export interface DashboardDataDTO {
  title: string;
}


export interface DashboardInitError {
  message: string;
  error: any;
}


export interface DashboardState {
  model: MutableDashboard | null;
  initPhase: DashboardInitPhase;
  isInitSlow: boolean;
  initError?: DashboardInitError;
  permissions: DashboardAcl[] | null;
  modifiedQueries?: {
    panelId: number;
    queries: DataQuery[];
  };
}


export enum DashboardRouteInfo {
  Home = 'home-dashboard',
  New = 'new-dashboard',
  Normal = 'normal-dashboard',
  Scripted = 'scripted-dashboard',
}


export enum DashboardInitPhase {
  NotStarted = 'Not started',
  Fetching = 'Fetching',
  Services = 'Services',
  Failed = 'Failed',
  Completed = 'Completed',
}


export type KioskUrlValue = 'tv' | '1' | true;


export function useDialog(): [(container: ReactNode, onExited?: VoidFunction) => void, VoidFunction] {
  const [createDialog, closeDialog] = React.useContext(DialogContext);
  let idDialog: number = -1;

  const handleOpen = useCallback(
    (container: ReactNode, onExited?: VoidFunction) => {
      idDialog = ++idd as number;
      createDialog({
        children: container,
        onExited,
        idDialog,
      });
    },
    [createDialog],
  );

  const handleClose = useCallback(() => {
    closeDialog(idDialog);
  }, [closeDialog, idDialog]);

  return [handleOpen, handleClose];
}


function DialogContainer({ children, open, onClose, onEnded }: DialogContainerProps) {
  return (
    <Dialog open={open} onClose={onClose} onEnded={onEnded}>
      {children}
    </Dialog>
  );
}


export default function DialogProvider({ children }: PropsWithChildren<{}>) {
  const [dialogs, setDialogs] = React.useState<DialogParams[]>([]);
  const [dialogsToRemove, setDialogsToRemove] = React.useState<number[]>([]);

  useEffect(() => {
    function removeDialog() {
      if (dialogsToRemove.length > 0) {
        setDialogs((dial) =>
          dial.map((dialog) => {
            if (dialog?.idDialog !== undefined && dialogsToRemove.indexOf(dialog.idDialog) !== -1) {
              return { ...dialog, open: false };
            }
            return dialog;
          }),
        );
        setDialogsToRemove([]);
      }
    }

    removeDialog();
  }, [dialogsToRemove]);

  function createDialog(option: DialogParams): void {
    return setDialogs((dial) => [...dial, { ...option, open: true }]);
  }

  function closeDialog(idDialog?: number): void {
    setDialogsToRemove((dial) => {
      if (idDialog === undefined) {
        const id = dialogs[dialogs.length - 1]?.idDialog;
        if (id === undefined) {
          return dial;
        }

        return [...dial, id];
      }

      return [...dial, idDialog];
    });
  }

  const contextValue = React.useRef([createDialog, closeDialog] as const);

  return (
    <DialogContext.Provider value={contextValue.current}>
      {children}
      {dialogs.map((dialog) => {
        const { onClose, ...dialogParams } = dialog;

        const handleOnEnded = () => {
          if (dialog.onExited) {
            dialog.onExited();
          }
          setDialogs((dial) => dial.slice(0, dial.length - 1));
        };

        return (
          <DialogContainer key={dialog.idDialog} onClose={() => closeDialog()} onEnded={handleOnEnded} open={dialogParams.open ?? false}>
            {dialogParams.children}
          </DialogContainer>
        );
      })}


function removeDialog() {
      if (dialogsToRemove.length > 0) {
        setDialogs((dial) =>
          dial.map((dialog) => {
            if (dialog?.idDialog !== undefined && dialogsToRemove.indexOf(dialog.idDialog) !== -1) {
              return { ...dialog, open: false };
            }
            return dialog;
          }),
        );
        setDialogsToRemove([]);
      }
    }


function createDialog(option: DialogParams): void {
    return setDialogs((dial) => [...dial, { ...option, open: true }]);
  }


function closeDialog(idDialog?: number): void {
    setDialogsToRemove((dial) => {
      if (idDialog === undefined) {
        const id = dialogs[dialogs.length - 1]?.idDialog;
        if (id === undefined) {
          return dial;
        }

        return [...dial, id];
      }

      return [...dial, idDialog];
    });
  }


(container: ReactNode, onExited?: VoidFunction) => {
      idDialog = ++idd as number;
      createDialog({
        children: container,
        onExited,
        idDialog,
      });
    }


() => {
    closeDialog(idDialog);
  }


() => {
    function removeDialog() {
      if (dialogsToRemove.length > 0) {
        setDialogs((dial) =>
          dial.map((dialog) => {
            if (dialog?.idDialog !== undefined && dialogsToRemove.indexOf(dialog.idDialog) !== -1) {
              return { ...dialog, open: false };
            }
            return dialog;
          }),
        );
        setDialogsToRemove([]);
      }
    }

    removeDialog();
  }


(dial) =>
          dial.map((dialog) => {
            if (dialog?.idDialog !== undefined && dialogsToRemove.indexOf(dialog.idDialog) !== -1) {
              return { ...dialog, open: false };
            }
            return dialog;
          })


(dialog) => {
            if (dialog?.idDialog !== undefined && dialogsToRemove.indexOf(dialog.idDialog) !== -1) {
              return { ...dialog, open: false };
            }
            return dialog;
          }


(dial) => [...dial, { ...option, open: true }]


(dial) => {
      if (idDialog === undefined) {
        const id = dialogs[dialogs.length - 1]?.idDialog;
        if (id === undefined) {
          return dial;
        }

        return [...dial, id];
      }

      return [...dial, idDialog];
    }


(dialog) => {
        const { onClose, ...dialogParams } = dialog;

        const handleOnEnded = () => {
          if (dialog.onExited) {
            dialog.onExited();
          }
          setDialogs((dial) => dial.slice(0, dial.length - 1));
        };

        return (
          <DialogContainer key={dialog.idDialog} onClose={() => closeDialog()}


() => {
          if (dialog.onExited) {
            dialog.onExited();
          }
          setDialogs((dial) => dial.slice(0, dial.length - 1));
        }


(dial) => dial.slice(0, dial.length - 1)


type DialogParams = PropsWithChildren<{
  idDialog?: number;
  open?: boolean;
  onClose?: VoidFunction;
  onExited?: VoidFunction;
}>;


type ProviderContext = readonly [(option: DialogParams) => void, (id?: number) => void];


type DialogContainerProps = PropsWithChildren<{
  open: boolean;
  onClose: VoidFunction;
  onEnded: VoidFunction;
}>;


export interface IMDIncGrp {
  MDUpdateAction: string// 279
  DeleteReason?: string// 285
  MDSubBookType?: number// 1173
  MarketDepth?: number// 264
  MDEntryType?: string// 269
  MDEntryID?: string// 278
  MDEntryRefID?: string// 280
  Instrument?: IInstrument
  UndInstrmtGrp?: IUndInstrmtGrp
  InstrmtLegGrp?: IInstrmtLegGrp
  FinancialStatus?: string// 291
  CorporateAction?: string// 292
  MDEntryPx?: number// 270
  PriceType?: number// 423
  YieldData?: IYieldData
  SpreadOrBenchmarkCurveData?: ISpreadOrBenchmarkCurveData
  OrdType?: string// 40
  Currency?: number// 15
  MDEntrySize?: number// 271
  SecSizesGrp?: ISecSizesGrp[]
  LotType?: string// 1093
  MDEntryDate?: Date// 272
  MDEntryTime?: Date// 273
  TickDirection?: string// 274
  MDMkt?: string// 275
  TradingSessionID?: string// 336
  TradingSessionSubID?: string// 625
  SecurityTradingStatus?: number// 326
  HaltReason?: number// 327
  QuoteCondition?: string// 276
  TradeCondition?: string// 277
  TrdType?: number// 828
  MatchType?: string// 574
  MDEntryOriginator?: string// 282
  LocationID?: string// 283
  DeskID?: string// 284
  OpenCloseSettlFlag?: string// 286
  TimeInForce?: string// 59
  ExpireDate?: Date// 432
  ExpireTime?: Date// 126
  MinQty?: number// 110
  ExecInst?: string// 18
  SellerDays?: number// 287
  OrderID?: string// 37
  SecondaryOrderID?: string// 198
  QuoteEntryID?: string// 299
  TradeID?: string// 1003
  MDEntryBuyer?: string// 288
  MDEntrySeller?: string// 289
  NumberOfOrders?: number// 346
  MDEntryPositionNo?: number// 290
  Scope?: string// 546
  PriceDelta?: number// 811
  NetChgPrevDay?: number// 451
  Text?: string// 58
  EncodedTextLen?: number// 354
  EncodedText?: Buffer// 355
  MDPriceLevel?: number// 1023
  OrderCapacity?: string// 528
  MDOriginType?: number// 1024
  HighPx?: number// 332
  LowPx?: number// 333
  TradeVolume?: number// 1020
  SettlType?: string// 63
  SettlDate?: Date// 64
  TransBkdTime?: Date// 483
  TransactTime?: Date// 60
  MDQuoteType?: number// 1070
  RptSeq?: number// 83
  DealingCapacity?: string// 1048
  MDEntrySpotRate?: number// 1026
  MDEntryForwardPoints?: number// 1027
  StatsIndGrp?: IStatsIndGrp[]
  Parties?: IParties[]
  SettlCurrency?: number// 120
  RateSource?: IRateSource[]
  FirstPx?: number// 1025
  LastPx?: number// 31
  MDStreamID?: string// 1500
}


() => import('@/uiservice/sys-role-permission/sys-role-permission-ui-service')


() => import('@/uiservice/sys-user/sys-user-ui-service')


() => import('@/uiservice/sys-auth-log/sys-auth-log-ui-service')


() => import('@/uiservice/sys-user-auth/sys-user-auth-ui-service')


() => import('@/uiservice/sys-open-access/sys-open-access-ui-service')


() => import('@/uiservice/sys-user-role/sys-user-role-ui-service')


() => import('@/uiservice/sys-role/sys-role-ui-service')


() => import('@/uiservice/sys-permission/sys-permission-ui-service')


() => import('@/uiservice/sys-app/sys-app-ui-service')


/**
 * UI服务注册中心
 *
 * @export
 * @class UIServiceRegister
 */
export class UIServiceRegister {

    /**
     * 所有UI实体服务Map
     *
     * @protected
     * @type {*}
     * @memberof UIServiceRegister
     */
    protected allUIService: Map<string, () => Promise<any>> = new Map();

    /**
     * 已加载UI实体服务Map缓存
     *
     * @protected
     * @type {Map<string, any>}
     * @memberof UIServiceRegister
     */
    protected serviceCache: Map<string, any> = new Map();

    /**
     * Creates an instance of UIServiceRegister.
     * @memberof UIServiceRegister
     */
    constructor() {
        this.init();
    }

    /**
     * 初始化
     *
     * @protected
     * @memberof UIServiceRegister
     */
    protected init(): void {
                this.allUIService.set('sysrolepermission', () => import('@/uiservice/sys-role-permission/sys-role-permission-ui-service'));
        this.allUIService.set('sysuser', () => import('@/uiservice/sys-user/sys-user-ui-service'));
        this.allUIService.set('sysauthlog', () => import('@/uiservice/sys-auth-log/sys-auth-log-ui-service'));
        this.allUIService.set('sysuserauth', () => import('@/uiservice/sys-user-auth/sys-user-auth-ui-service'));
        this.allUIService.set('sysopenaccess', () => import('@/uiservice/sys-open-access/sys-open-access-ui-service'));
        this.allUIService.set('sysuserrole', () => import('@/uiservice/sys-user-role/sys-user-role-ui-service'));
        this.allUIService.set('sysrole', () => import('@/uiservice/sys-role/sys-role-ui-service'));
        this.allUIService.set('syspermission', () => import('@/uiservice/sys-permission/sys-permission-ui-service'));
        this.allUIService.set('sysapp', () => import('@/uiservice/sys-app/sys-app-ui-service'));
    }

    /**
     * 加载服务实体
     *
     * @protected
     * @param {string} serviceName
     * @returns {Promise<any>}
     * @memberof UIServiceRegister
     */
    protected async loadService(serviceName: string): Promise<any> {
        const service = this.allUIService.get(serviceName);
        if (service) {
            return service();
        }
    }

    /**
     * 获取应用实体服务
     *
     * @param {string} name
     * @returns {Promise<any>}
     * @memberof UIServiceRegister
     */
    public async getService(name: string): Promise<any> {
        if (this.serviceCache.has(name)) {
            return this.serviceCache.get(name);
        }
        const entityService: any = await this.loadService(name);
        if (entityService && entityService.default) {
            const instance: any = new entityService.default();
            this.serviceCache.set(name, instance);
            return instance;
        }
    }

}


/**
     * 初始化
     *
     * @protected
     * @memberof UIServiceRegister
     */
protected init(): void {
                this.allUIService.set('sysrolepermission', () => import('@/uiservice/sys-role-permission/sys-role-permission-ui-service'));
        this.allUIService.set('sysuser', () => import('@/uiservice/sys-user/sys-user-ui-service'));
        this.allUIService.set('sysauthlog', () => import('@/uiservice/sys-auth-log/sys-auth-log-ui-service'));
        this.allUIService.set('sysuserauth', () => import('@/uiservice/sys-user-auth/sys-user-auth-ui-service'));
        this.allUIService.set('sysopenaccess', () => import('@/uiservice/sys-open-access/sys-open-access-ui-service'));
        this.allUIService.set('sysuserrole', () => import('@/uiservice/sys-user-role/sys-user-role-ui-service'));
        this.allUIService.set('sysrole', () => import('@/uiservice/sys-role/sys-role-ui-service'));
        this.allUIService.set('syspermission', () => import('@/uiservice/sys-permission/sys-permission-ui-service'));
        this.allUIService.set('sysapp', () => import('@/uiservice/sys-app/sys-app-ui-service'));
    }


/**
     * 加载服务实体
     *
     * @protected
     * @param {string} serviceName
     * @returns {Promise<any>}
     * @memberof UIServiceRegister
     */
protected async loadService(serviceName: string): Promise<any> {
        const service = this.allUIService.get(serviceName);
        if (service) {
            return service();
        }
    }


/**
     * 获取应用实体服务
     *
     * @param {string} name
     * @returns {Promise<any>}
     * @memberof UIServiceRegister
     */
public async getService(name: string): Promise<any> {
        if (this.serviceCache.has(name)) {
            return this.serviceCache.get(name);
        }
        const entityService: any = await this.loadService(name);
        if (entityService && entityService.default) {
            const instance: any = new entityService.default();
            this.serviceCache.set(name, instance);
            return instance;
        }
    }


(resolve) => {
            this.http.post(url, data, { headers: this.getHeader() }).subscribe(
                (response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                },
                (error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };
                    this.errorValidate(resultError);
                    resolve(resultError);
                });
        }


(response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                }


(error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };
                    this.errorValidate(resultError);
                    resolve(resultError);
                }


(resolve) => {
            this.http.get(url, { headers: this.getHeader() }).subscribe(
                (response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                },
                (error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };

                    this.errorValidate(resultError);
                    resolve(resultError);
                });
        }


(error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };

                    this.errorValidate(resultError);
                    resolve(resultError);
                }


@Injectable()
export class CommunicationService {

    constructor(public http: HttpClient, public router: Router) { }

    public post<TModel>(url: string, data: any): Promise<ICommunication<TModel>> {
        return new Promise((resolve) => {
            this.http.post(url, data, { headers: this.getHeader() }).subscribe(
                (response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                },
                (error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };
                    this.errorValidate(resultError);
                    resolve(resultError);
                });
        });
    }

    public get<TModel>(url: string): Promise<ICommunication<TModel>> {
        return new Promise((resolve) => {
            this.http.get(url, { headers: this.getHeader() }).subscribe(
                (response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                },
                (error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };

                    this.errorValidate(resultError);
                    resolve(resultError);
                });
        });
    }

    getHeader(): HttpHeaders { return new HttpHeaders().set("Authorization", "Bearer " + this.getToken()).append('Content-Type', 'application/json'); }

    public getToken() {
        let info = this.getLocalStorageToken();
        if (info) return info['access_token'];
    }

    public getUserInfo() {
        let info = this.getLocalStorageToken();
        if (info) { return { id: info["session_id"], username: info["username"], name: info["name"] }; }
    }

    private getLocalStorageToken() {
        let local = localStorage.getItem(Constants.STORAGE_USER_SESSION);
        if (isNullOrUndefined(local)) return null;
        let obj = JSON.parse(local);
        if (isNullOrUndefined(obj)) return null;
        return obj;
    }

    private errorValidate(result: any) {
        if (!result.success) {
            if (!isNullOrUndefined(result.modelState)) {
                if (result.modelState.message == "USR_LOGIN") { this.router.navigate(['/entrar']); }
                //adicionar demais erros aqui
            }
        }
    }
}


public post<TModel>(url: string, data: any): Promise<ICommunication<TModel>> {
        return new Promise((resolve) => {
            this.http.post(url, data, { headers: this.getHeader() }).subscribe(
                (response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                },
                (error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };
                    this.errorValidate(resultError);
                    resolve(resultError);
                });
        });
    }


public get<TModel>(url: string): Promise<ICommunication<TModel>> {
        return new Promise((resolve) => {
            this.http.get(url, { headers: this.getHeader() }).subscribe(
                (response) => {
                    resolve(<ICommunication<TModel>>{
                        success: true,
                        data: response
                    });
                },
                (error) => {
                    let resultError = <ICommunication<TModel>>{
                        success: false,
                        statusCode: error.status,
                        data: error.error
                    };

                    this.errorValidate(resultError);
                    resolve(resultError);
                });
        });
    }


getHeader(): HttpHeaders { return new HttpHeaders().set("Authorization", "Bearer " + this.getToken()).append('Content-Type', 'application/json'); }


public getToken() {
        let info = this.getLocalStorageToken();
        if (info) return info['access_token'];
    }


public getUserInfo() {
        let info = this.getLocalStorageToken();
        if (info) { return { id: info["session_id"], username: info["username"], name: info["name"] }; }
    }


private getLocalStorageToken() {
        let local = localStorage.getItem(Constants.STORAGE_USER_SESSION);
        if (isNullOrUndefined(local)) return null;
        let obj = JSON.parse(local);
        if (isNullOrUndefined(obj)) return null;
        return obj;
    }


private errorValidate(result: any) {
        if (!result.success) {
            if (!isNullOrUndefined(result.modelState)) {
                if (result.modelState.message == "USR_LOGIN") { this.router.navigate(['/entrar']); }
                //adicionar demais erros aqui
            }
        }
    }


@Component({
  selector: "ngx-powerbi-component",
  template: '<div #ngxPowerBiIFrame style="height:100%; width: 100%;"></div>',
  styles: [],
})
export class NgxPowerBiComponent
  implements AfterViewInit, OnChanges, OnDestroy
{
  // Public properties
  @Input() accessToken: string;
  @Input() tokenType: TokenType;
  @Input() embedUrl: string;
  @Input() id: string;
  @Input() type: ReportType;
  @Input() name: string;
  @Input() viewMode: number = models.ViewMode.View;
  @Input() options: models.ISettings;
  @Output() embedded = new EventEmitter<number>();
  @ViewChild("ngxPowerBiIFrame", { static: true })
  ngxPowerBiIFrameRef: ElementRef;

  // Private fields
  private component: pbi.Embed;
  private powerBiService: NgxPowerBiService;

  constructor(private ngxPowerBiService: NgxPowerBiService) {
    this.powerBiService = ngxPowerBiService;
  }

  ngAfterViewInit() {
    // Embed the report inside the view child that we have fetched from the DOM
    if (
      this.ngxPowerBiIFrameRef.nativeElement &&
      this.validateRequiredAttributes()
    ) {
      this.embed(this.ngxPowerBiIFrameRef.nativeElement, this.getConfig());
    }
  }

  ngOnChanges(changes: SimpleChanges) {
    if (!this.ngxPowerBiIFrameRef) {
      return;
    }
    const {
      accessToken,
      tokenType,
      embedUrl,
      id,
      type,
      name,
      options,
      viewMode,
    } = changes;

    // TODO: Validate these conditions
    /*if (
      (accessToken.previousValue !== undefined || accessToken.previousValue === accessToken.currentValue) &&
      embedUrl.previousValue === embedUrl.currentValue
    ) {
      return;
    }*/

    if (this.validateRequiredAttributes()) {
      const config = this.getConfig(
        accessToken && accessToken.currentValue,
        tokenType && tokenType.currentValue,
        embedUrl && embedUrl.currentValue,
        id && id.currentValue,
        type && type.currentValue,
        name && name.currentValue,
        options && options.currentValue,
        viewMode && viewMode.currentValue
      );
      this.embed(this.ngxPowerBiIFrameRef.nativeElement, config);
    } else if (this.component) {
      this.reset(this.ngxPowerBiIFrameRef.nativeElement);
    }
  }

  ngOnDestroy() {
    if (this.component) {
      this.reset(this.ngxPowerBiIFrameRef.nativeElement);
    }
  }

  /**
   * Ensure required attributes (embedUrl and accessToken are valid before attempting to embed)
   */
  private validateRequiredAttributes(): boolean {
    return (
      typeof this.embedUrl === "string" &&
      this.embedUrl.length > 0 &&
      typeof this.accessToken === "string" &&
      this.accessToken.length > 0
    );
  }

  /**
   * Get the model class compatible token enum from our token type enum
   * @param tokenType - Token type in our custom enum format
   * @returns Token type in powerbi-models.TokenType enum format
   */
  private getTokenType(tokenType: TokenType): models.TokenType {
    if (tokenType) {
      switch (tokenType) {
        case TokenType.Aad:
          return models.TokenType.Aad;
        case TokenType.Embed:
          return models.TokenType.Embed;
        default:
          return models.TokenType.Aad;
      }
    } else {
      // default is AAD
      return models.TokenType.Aad;
    }
  }

  /**
   * Returns an embed configuration object.
   * @param accessToken - Access token required to embed a component
   * @param tokenType - type of accessToken: Aad or Embed
   * @param embedUrl - Embed URL obtained through Power BI REST API or Portal
   * @param id - component/element GUID
   * @param type - type of embedded component e.g. 'dashboard, report or tile'
   * @param name - name of the embedded component
   * @param options - Embed configuration options
   * @returns Embed configuration object
   */
  private getConfig(
    accessToken?: string,
    tokenType?: TokenType,
    embedUrl?: string,
    id?: string,
    type?: ReportType,
    name?: string,
    options?: models.ISettings,
    viewMode?: models.ViewMode
  ): pbi.IEmbedConfiguration {
    // For null arguments - use the initial value
    // For specified arguments - use the current value
    return {
      type: type ? type : this.type ? this.type : ReportType.Report,
      embedUrl: embedUrl ? embedUrl : this.embedUrl,
      accessToken: accessToken ? accessToken : this.accessToken,
      tokenType: tokenType
        ? this.getTokenType(tokenType)
        : this.getTokenType(this.tokenType),
      id: id ? id : this.id,
      uniqueId: name ? name : this.name,
      viewMode: viewMode ? viewMode : this.viewMode,
      settings: options ? options : this.options,
    };
  }

  /**
   * Given an HTMLElement, construct an embed configuration based on attributes and pass to service.
   * @param element - native element where the embedding needs to be done
   * @param config - configuration to be embedded
   */
  private embed(element: HTMLElement, config: pbi.IEmbedConfiguration) {
    /*if (this.options) {
      const newConfig = { config, ...this.options };
    }*/

    this.component = this.powerBiService.embed(element, config);
    this.embedded.emit(this.component as any);
  }

  /**
   * Reset the component that has been removed from DOM.
   * @param element - native element where the embedded was made
   */
  reset(element: HTMLElement) {
    this.powerBiService.reset(element);
    this.component = null;
  }
}


export const enum TokenType {
  Aad = "Aad",
  Embed = "Embed",
}


export const enum ReportType {
  Dashboard = "Dashboard",
  Report = "Report",
  Tile = "Tile",
}


ngAfterViewInit() {
    // Embed the report inside the view child that we have fetched from the DOM
    if (
      this.ngxPowerBiIFrameRef.nativeElement &&
      this.validateRequiredAttributes()
    ) {
      this.embed(this.ngxPowerBiIFrameRef.nativeElement, this.getConfig());
    }
  }


ngOnChanges(changes: SimpleChanges) {
    if (!this.ngxPowerBiIFrameRef) {
      return;
    }
    const {
      accessToken,
      tokenType,
      embedUrl,
      id,
      type,
      name,
      options,
      viewMode,
    } = changes;

    // TODO: Validate these conditions
    /*if (
      (accessToken.previousValue !== undefined || accessToken.previousValue === accessToken.currentValue) &&
      embedUrl.previousValue === embedUrl.currentValue
    ) {
      return;
    }*/

    if (this.validateRequiredAttributes()) {
      const config = this.getConfig(
        accessToken && accessToken.currentValue,
        tokenType && tokenType.currentValue,
        embedUrl && embedUrl.currentValue,
        id && id.currentValue,
        type && type.currentValue,
        name && name.currentValue,
        options && options.currentValue,
        viewMode && viewMode.currentValue
      );
      this.embed(this.ngxPowerBiIFrameRef.nativeElement, config);
    } else if (this.component) {
      this.reset(this.ngxPowerBiIFrameRef.nativeElement);
    }
  }


ngOnDestroy() {
    if (this.component) {
      this.reset(this.ngxPowerBiIFrameRef.nativeElement);
    }
  }


/**
   * Ensure required attributes (embedUrl and accessToken are valid before attempting to embed)
   */
private validateRequiredAttributes(): boolean {
    return (
      typeof this.embedUrl === "string" &&
      this.embedUrl.length > 0 &&
      typeof this.accessToken === "string" &&
      this.accessToken.length > 0
    );
  }


/**
   * Get the model class compatible token enum from our token type enum
   * @param tokenType - Token type in our custom enum format
   * @returns Token type in powerbi-models.TokenType enum format
   */
private getTokenType(tokenType: TokenType): models.TokenType {
    if (tokenType) {
      switch (tokenType) {
        case TokenType.Aad:
          return models.TokenType.Aad;
        case TokenType.Embed:
          return models.TokenType.Embed;
        default:
          return models.TokenType.Aad;
      }
    } else {
      // default is AAD
      return models.TokenType.Aad;
    }
  }


/**
   * Returns an embed configuration object.
   * @param accessToken - Access token required to embed a component
   * @param tokenType - type of accessToken: Aad or Embed
   * @param embedUrl - Embed URL obtained through Power BI REST API or Portal
   * @param id - component/element GUID
   * @param type - type of embedded component e.g. 'dashboard, report or tile'
   * @param name - name of the embedded component
   * @param options - Embed configuration options
   * @returns Embed configuration object
   */
private getConfig(
    accessToken?: string,
    tokenType?: TokenType,
    embedUrl?: string,
    id?: string,
    type?: ReportType,
    name?: string,
    options?: models.ISettings,
    viewMode?: models.ViewMode
  ): pbi.IEmbedConfiguration {
    // For null arguments - use the initial value
    // For specified arguments - use the current value
    return {
      type: type ? type : this.type ? this.type : ReportType.Report,
      embedUrl: embedUrl ? embedUrl : this.embedUrl,
      accessToken: accessToken ? accessToken : this.accessToken,
      tokenType: tokenType
        ? this.getTokenType(tokenType)
        : this.getTokenType(this.tokenType),
      id: id ? id : this.id,
      uniqueId: name ? name : this.name,
      viewMode: viewMode ? viewMode : this.viewMode,
      settings: options ? options : this.options,
    };
  }


/**
   * Given an HTMLElement, construct an embed configuration based on attributes and pass to service.
   * @param element - native element where the embedding needs to be done
   * @param config - configuration to be embedded
   */
private embed(element: HTMLElement, config: pbi.IEmbedConfiguration) {
    /*if (this.options) {
      const newConfig = { config, ...this.options };
    }*/

    this.component = this.powerBiService.embed(element, config);
    this.embedded.emit(this.component as any);
  }


/**
   * Reset the component that has been removed from DOM.
   * @param element - native element where the embedded was made
   */
reset(element: HTMLElement) {
    this.powerBiService.reset(element);
    this.component = null;
  }


() => {
  let component: PresupuestoContainerComponent;
  let fixture: ComponentFixture<PresupuestoContainerComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ PresupuestoContainerComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(PresupuestoContainerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
}


() => {
    TestBed.configureTestingModule({
      declarations: [ PresupuestoContainerComponent ]
    })
    .compileComponents();
  }


() => {
    fixture = TestBed.createComponent(PresupuestoContainerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }


(lang: string): void => {
    document.querySelector("html")?.setAttribute("lang", lang);

    const defaultVariables = defaultVars[lang] || defaultVars.en;
    if (i18next.options.interpolation) {
        i18next.options.interpolation.defaultVariables = defaultVariables;
    } else {
        i18next.options.interpolation = { defaultVariables };
    }
}


function SvgCat(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg
      data-name='Layer 3'
      xmlns='http://www.w3.org/2000/svg'
      viewBox='0 0 24 24'
      width='1em'
      height='1em'
      {...props}
    >
      <path
        d='M15.074 6.42l2.389-2.047A1.537 1.537 0 0120 5.54V13a8 8 0 01-8 8h0a8 8 0 01-8-8V5.54a1.537 1.537 0 012.537-1.167l2.389 2.046a9.223 9.223 0 016.148 0z'
        fill='none'
        stroke='currentColor'
        strokeLinecap='round'
        strokeLinejoin='round'
        strokeWidth={1.5}
      />
      <path
        d='M10.25 10.75a.25.25 0 11-.25-.25.25.25 0 01.25.25M14.25 10.75a.25.25 0 11-.25-.25.25.25 0 01.25.25M12 14.5h0a.5.5 0 01-.5-.5h0a.167.167 0 01.167-.167h.666A.167.167 0 0112.5 14h0a.5.5 0 01-.5.5zM9.813 17.125a1.277 1.277 0 00.906.375h0A1.281 1.281 0 0012 16.219a1.281 1.281 0 002.187.906M12 14.5v1.719M17 12.807l4-.667M21 16l-4-.667M7 12.807l-4-.667M3 16l4-.667'
        fill='none'
        stroke='currentColor'
        strokeLinecap='round'
        strokeLinejoin='round'
        strokeWidth={1.5}
      />
      <path fill='none' d='M0 0h24v24H0z' />
    </svg>
  )
}


() => {
  store = createStore(clone(wpSource()));
  store.state.source.api = "https://test.frontity.org/wp-json";
  store.actions.source.init();
  api = store.libraries.source.api as jest.Mocked<Api>;
}


() => {
  test("returns 404 if not found", async () => {
    // Mock Api responses
    // We have to use this form instead of:
    // .mockResolvedValueOnce(mockResponse([]))
    // because the latter always returns the same instance of Response.
    // which results in error because response.json() can only be run once
    api.get = jest.fn((_) => Promise.resolve(mockResponse([])));

    // Fetch entities
    await store.actions.source.fetch("/non-existent/");
    expect(api.get).toHaveBeenCalledTimes(3);
    expect(store.state.source).toMatchSnapshot();
  });

  test("should contain the correct error code on error", async () => {
    // Mock Api responses
    api.get = jest.fn(async (_) => {
      throw new ServerError("statusText", 400, "statusText");
    });

    // Fetch entities
    await store.actions.source.fetch("/post-1/");

    expect(store.state.source.data["/post-1/"].isError).toBe(true);
    expect(store.state.source.data["/post-1/"]["is400"]).toBe(true);
    expect(store.state.source).toMatchSnapshot();
  });
}


async () => {
    // Mock Api responses
    // We have to use this form instead of:
    // .mockResolvedValueOnce(mockResponse([]))
    // because the latter always returns the same instance of Response.
    // which results in error because response.json() can only be run once
    api.get = jest.fn((_) => Promise.resolve(mockResponse([])));

    // Fetch entities
    await store.actions.source.fetch("/non-existent/");
    expect(api.get).toHaveBeenCalledTimes(3);
    expect(store.state.source).toMatchSnapshot();
  }


(_) => Promise.resolve(mockResponse([]))


async () => {
    // Mock Api responses
    api.get = jest.fn(async (_) => {
      throw new ServerError("statusText", 400, "statusText");
    });

    // Fetch entities
    await store.actions.source.fetch("/post-1/");

    expect(store.state.source.data["/post-1/"].isError).toBe(true);
    expect(store.state.source.data["/post-1/"]["is400"]).toBe(true);
    expect(store.state.source).toMatchSnapshot();
  }


async (_) => {
      throw new ServerError("statusText", 400, "statusText");
    }


() => {
  test("doesn't exist in source.post", async () => {
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([post1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/");
    expect(store.state.source).toMatchSnapshot();
  });

  test("exists in source.post", async () => {
    // Add post to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(post1),
    });
    // Mock Api responses
    api.get = jest.fn();
    // Fetch entities
    await store.actions.source.fetch("/post-1/");
    expect(api.get).not.toHaveBeenCalled();
    expect(store.state.source).toMatchSnapshot();
  });

  test("fetchs from a different endpoint with extra params", async () => {
    // Add custom post endpoint and params
    store.state.source.postEndpoint = "multiple-post-type";
    store.state.source.params = { type: ["post", "cpt"] };
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([cpt11]));
    // Fetch entities
    await store.actions.source.fetch("/cpt/cpt-11");
    expect(api.get.mock.calls).toMatchSnapshot();
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with query params (doesn't exist in source.post)", async () => {
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([post1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/?some=param");
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with query params (exists in source.post)", async () => {
    // Add post to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(post1),
    });
    // Mock Api responses
    api.get = jest.fn();
    // Fetch entities
    await store.actions.source.fetch("/post-1/?some=param");
    expect(api.get).not.toHaveBeenCalled();
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with types embedded", async () => {
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([post1withType]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/");
    expect(store.state.source).toMatchSnapshot();
  });
}


async () => {
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([post1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/");
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add post to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(post1),
    });
    // Mock Api responses
    api.get = jest.fn();
    // Fetch entities
    await store.actions.source.fetch("/post-1/");
    expect(api.get).not.toHaveBeenCalled();
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add custom post endpoint and params
    store.state.source.postEndpoint = "multiple-post-type";
    store.state.source.params = { type: ["post", "cpt"] };
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([cpt11]));
    // Fetch entities
    await store.actions.source.fetch("/cpt/cpt-11");
    expect(api.get.mock.calls).toMatchSnapshot();
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([post1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/?some=param");
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add post to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(post1),
    });
    // Mock Api responses
    api.get = jest.fn();
    // Fetch entities
    await store.actions.source.fetch("/post-1/?some=param");
    expect(api.get).not.toHaveBeenCalled();
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([post1withType]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/");
    expect(store.state.source).toMatchSnapshot();
  }


() => {
  test("doesn't exist in source.page", async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([page1]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/");
    expect(store.state.source).toMatchSnapshot();
  });

  test("exists in source.page", async () => {
    // Add page to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(page1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with query params (doesn't exist in source.page)", async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([page1]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/?some=param");
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with query params (exists in source.page)", async () => {
    // Add page to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(page1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/?some=param");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  });
}


async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([page1]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/");
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add page to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(page1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([page1]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/?some=param");
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add page to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(page1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValueOnce(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/page-1/?some=param");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  }


() => {
  test("doesn't exist in source.attachment", async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([attachment1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/");
    expect(store.state.source).toMatchSnapshot();
  });

  test("exists in source.attachment", async () => {
    // Add attachment to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(attachment1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValue(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with query params (doesn't exist in source.attachment)", async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([attachment1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/?some=param");
    expect(store.state.source).toMatchSnapshot();
  });

  test("works with query params (exists in source.attachment)", async () => {
    // Add attachment to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(attachment1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValue(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/?some=param");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  });

  test("overwrites the data when fetched with { force: true }", async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([post1]))
      .mockResolvedValueOnce(mockResponse(attachment1));

    // Fetch entities
    await store.actions.source.fetch("/post-1");
    await store.actions.source.fetch("/post-1", { force: true });

    expect(store.state.source).toMatchSnapshot();
  });

  test("Every unknown URL should return a 404 even if it's substring matches a path", async () => {
    api.get = jest.fn((_) =>
      Promise.resolve(
        mockResponse([
          {
            id: 1,
            slug: "post-1",
            type: "post",
            link: "https://test.frontity.org/post-1/",
          },
        ])
      )
    );

    await store.actions.source.fetch("/undefined/post-1/");

    expect(store.state.source).toMatchSnapshot();
  });

  test("Every unknown URL should return a 404 even if it's substring matches a path 2", async () => {
    api.get = jest.fn((_) =>
      Promise.resolve(
        mockResponse([
          {
            id: 1,
            slug: "post-1",
            type: "post",
            link: "https://test.frontity.org/post-1/",
          },
        ])
      )
    );

    await store.actions.source.fetch("/does/not/exist/");

    expect(store.state.source).toMatchSnapshot();
  });
}


async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([attachment1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/");
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add attachment to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(attachment1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValue(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([]))
      .mockResolvedValueOnce(mockResponse([attachment1]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/?some=param");
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Add attachment to the store
    await store.libraries.source.populate({
      state: store.state,
      response: mockResponse(attachment1),
    });
    // Mock Api responses
    api.get = jest.fn().mockResolvedValue(mockResponse([]));
    // Fetch entities
    await store.actions.source.fetch("/post-1/attachment-1/?some=param");
    expect(api.get).toHaveBeenCalledTimes(0);
    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    // Mock Api responses
    api.get = jest
      .fn()
      .mockResolvedValueOnce(mockResponse([post1]))
      .mockResolvedValueOnce(mockResponse(attachment1));

    // Fetch entities
    await store.actions.source.fetch("/post-1");
    await store.actions.source.fetch("/post-1", { force: true });

    expect(store.state.source).toMatchSnapshot();
  }


async () => {
    api.get = jest.fn((_) =>
      Promise.resolve(
        mockResponse([
          {
            id: 1,
            slug: "post-1",
            type: "post",
            link: "https://test.frontity.org/post-1/",
          },
        ])
      )
    );

    await store.actions.source.fetch("/undefined/post-1/");

    expect(store.state.source).toMatchSnapshot();
  }


(_) =>
      Promise.resolve(
        mockResponse([
          {
            id: 1,
            slug: "post-1",
            type: "post",
            link: "https://test.frontity.org/post-1/",
          },
        ])
      )


async () => {
    api.get = jest.fn((_) =>
      Promise.resolve(
        mockResponse([
          {
            id: 1,
            slug: "post-1",
            type: "post",
            link: "https://test.frontity.org/post-1/",
          },
        ])
      )
    );

    await store.actions.source.fetch("/does/not/exist/");

    expect(store.state.source).toMatchSnapshot();
  }


/**
 * Given a URL pointing to a file on a provider, returns a URL that is suitable
 * for fetching the contents of the data.
 *
 * Converts
 * from: https://dev.azure.com/{organization}/{project}/_git/reponame?path={path}&version=GB{commitOrBranch}&_a=contents
 * to:   https://dev.azure.com/{organization}/{project}/_apis/git/repositories/reponame/items?path={path}&version={commitOrBranch}
 *
 * @param url A URL pointing to a file
 */
export function getAzureFileFetchUrl(url: string): string {
  try {
    const parsedUrl = new URL(url);

    const [
      empty,
      userOrOrg,
      project,
      srcKeyword,
      repoName,
    ] = parsedUrl.pathname.split('/');

    const path = parsedUrl.searchParams.get('path') || '';
    const ref = parsedUrl.searchParams.get('version')?.substr(2);

    if (
      empty !== '' ||
      userOrOrg === '' ||
      project === '' ||
      srcKeyword !== '_git' ||
      repoName === '' ||
      path === '' ||
      ref === ''
    ) {
      throw new Error('Wrong Azure Devops URL or Invalid file path');
    }

    // transform to api
    parsedUrl.pathname = [
      empty,
      userOrOrg,
      project,
      '_apis',
      'git',
      'repositories',
      repoName,
      'items',
    ].join('/');

    const queryParams = [`path=${path}`];

    if (ref) {
      queryParams.push(`version=${ref}`);
    }

    parsedUrl.search = queryParams.join('&');

    parsedUrl.protocol = 'https';

    return parsedUrl.toString();
  } catch (e) {
    throw new Error(`Incorrect URL: ${url}, ${e}`);
  }
}


/**
 * Given a URL pointing to a path on a provider, returns a URL that is suitable
 * for downloading the subtree.
 *
 * @param url A URL pointing to a path
 */
export function getAzureDownloadUrl(url: string): string {
  const {
    name: repoName,
    owner: project,
    organization,
    protocol,
    resource,
    filepath,
  } = parseGitUrl(url);

  // scopePath will limit the downloaded content
  // /docs will only download the docs folder and everything below it
  // /docs/index.md will only download index.md but put it in the root of the archive
  const scopePath = filepath
    ? `&scopePath=${encodeURIComponent(filepath)}`
    : '';

  return `${protocol}://${resource}/${organization}/${project}/_apis/git/repositories/${repoName}/items?recursionLevel=full&download=true&api-version=6.0${scopePath}`;
}


/**
 * Gets the request options necessary to make requests to a given provider.
 *
 * @param config The relevant provider config
 */
export function getAzureRequestOptions(
  config: AzureIntegrationConfig,
  additionalHeaders?: Record<string, string>,
): RequestInit {
  const headers: HeadersInit = additionalHeaders
    ? { ...additionalHeaders }
    : {};

  if (config.token) {
    const buffer = Buffer.from(`:${config.token}`, 'utf8');
    headers.Authorization = `Basic ${buffer.toString('base64')}`;
  }

  return { headers };
}


export interface Card {
  title: string;
  type: CardType;
  icon: IconName;
  href: string;
  check: () => Promise<boolean>;
  done: boolean;
  heading: string;
  learnHref?: string;
}


export interface TutorialCardType extends Card {
  info?: string;
  // For local storage
  key: string;
}


export interface SetupStep {
  heading: string;
  subheading: string;
  title: string;
  info: string;
  cards: (Card | TutorialCardType)[];
  done: boolean;
}


export type CardType = 'tutorial' | 'docs' | 'other';


(props) => {
  // const [topics, setTopics] = useState<firebase.firestore.DocumentData>({});
  const { data, loading } = useContext(LadderContext);
  // useEffect(() => {
  //   const fetchData = () => {
  //     firestore
  //       .collection("ladders")
  //       .get()
  //       .then((ladderList) => {
  //         setTopics({ ...ladderList.docs[0].data().topics });
  //       })
  //       .catch((err) => console.log(err));
  //   };
  //   fetchData();
  //   //eslint-disable-next-line react-hooks/exhaustive-deps
  // }, []);
  return (
    <>
      {!loading && data[0] !== undefined ? (
        <div className="grid grid-cols-12 gap-2 p-5 rounded-md sm:space-x-0 sm:gap-4 sm:p-3">
          {Object.keys(data[0].topics).map((keyName, keyIndex) => {
            return (
              <Card
                key={keyIndex}
                title={keyName}
                questions={data[0].topics[keyName]}
                className="w-full col-span-12 sm:col-span-6 lg:col-span-4"
                topicLink={keyName}
              />
            );
          })}


(keyName, keyIndex) => {
            return (
              <Card
                key={keyIndex}
                title={keyName}
                questions={data[0].topics[keyName]}
                className="w-full col-span-12 sm:col-span-6 lg:col-span-4"
                topicLink={keyName}
              />


interface Props {}


function Demo() {
  return (
    <Group position="center">
      <Badge variant="gradient" gradient={{ from: 'indigo', to: 'cyan' }}


async (packet, referrerObj) => {
      const query = new Query(packet);
      const referrer = new Referrer(referrerObj);

      this.emit("query", query, referrer);
    }


(packet, referrerObj) => {
      const res = Response.parse(packet, { binaryTXT: this.options.binaryTXT });
      const referrer = new Referrer(referrerObj);

      this.emit("response", res, referrer);
    }


(resolve, reject) => {
      this.mdns.query(query, err => {
        if (err) return reject(err);
        resolve();
      });
    }


err => {
        if (err) return reject(err);
        resolve();
      }


(resolve, reject) => {
      this.mdns.respond(Response.serialize(res, { binaryTXT: this.options.binaryTXT }), err => {
        if (err) return reject(err);
        resolve();
      });
    }


(resolve, reject) => {
      this.mdns.destroy(err => {
        if (err) return reject(err);
        resolve();
      });
    }


export default class MDNSTransport extends EventEmitter implements Transport {
  options: TransportOptions;
  destroyed: boolean = false;
  // tslint:disable-next-line:no-any
  mdns: any;

  constructor(options: TransportOptions = {}) {
    super();

    this.options = options;
    this.mdns = multicastdns();
    this.mdns.setMaxListeners(0);
    this.mdns.on("query", async (packet, referrerObj) => {
      const query = new Query(packet);
      const referrer = new Referrer(referrerObj);

      this.emit("query", query, referrer);
    });

    this.mdns.on("response", (packet, referrerObj) => {
      const res = Response.parse(packet, { binaryTXT: this.options.binaryTXT });
      const referrer = new Referrer(referrerObj);

      this.emit("response", res, referrer);
    });
  }

  async query(query: Query) {
    return new Promise<void>((resolve, reject) => {
      this.mdns.query(query, err => {
        if (err) return reject(err);
        resolve();
      });
    });
  }

  async respond(res: Response) {
    return await new Promise<void>((resolve, reject) => {
      this.mdns.respond(Response.serialize(res, { binaryTXT: this.options.binaryTXT }), err => {
        if (err) return reject(err);
        resolve();
      });
    });
  }

  async destroy() {
    if (this.destroyed) return;
    this.destroyed = true;

    await new Promise<void>((resolve, reject) => {
      this.mdns.destroy(err => {
        if (err) return reject(err);
        resolve();
      });
    });
  }

  getAddresses(): Array<{ family: string, address: string }> {
    return MDNSUtils.getExternalAddresses();
  }
}


async query(query: Query) {
    return new Promise<void>((resolve, reject) => {
      this.mdns.query(query, err => {
        if (err) return reject(err);
        resolve();
      });
    });
  }


async respond(res: Response) {
    return await new Promise<void>((resolve, reject) => {
      this.mdns.respond(Response.serialize(res, { binaryTXT: this.options.binaryTXT }), err => {
        if (err) return reject(err);
        resolve();
      });
    });
  }


async destroy() {
    if (this.destroyed) return;
    this.destroyed = true;

    await new Promise<void>((resolve, reject) => {
      this.mdns.destroy(err => {
        if (err) return reject(err);
        resolve();
      });
    });
  }


getAddresses(): Array<{ family: string, address: string }> {
    return MDNSUtils.getExternalAddresses();
  }


function handleNavigateToBack() {
        navigation.goBack();
    }


function handleComposeMail() {
      MailComposer.composeAsync({
        subject: 'Gostaria de saber um pouco mais sobre a coleta de resíduos',
        recipients: [data.point.email]
      })
    }


function handleWhatsapp() {
      Linking.openURL(`whatsapp://send?phone=${data.point.whatsapp}&tenho interesse sobre a coleta de resíduos`);
    }


() => {
    const [data, setData] = useState<Data>({} as Data);
    const navigation = useNavigation();
    const route = useRoute();

    const routeParams = route.params as Params;

    useEffect(() => {
        api.get(`points/${routeParams.point_id}`).then(response => {
            setData(response.data)
        })
    })
    function handleNavigateToBack() {
        navigation.goBack();
    }

    function handleComposeMail() {
      MailComposer.composeAsync({
        subject: 'Gostaria de saber um pouco mais sobre a coleta de resíduos',
        recipients: [data.point.email]
      })
    }

    function handleWhatsapp() {
      Linking.openURL(`whatsapp://send?phone=${data.point.whatsapp}&tenho interesse sobre a coleta de resíduos`);
    }

    if(!data.point) {
        return null
    }
    return (
        <SafeAreaView style={{flex: 1}}


() => {
        api.get(`points/${routeParams.point_id}`).then(response => {
            setData(response.data)
        })
    }


response => {
            setData(response.data)
        }


item => item.title


interface Params {
    point_id: number;
}


interface Data {
    point: {
       image: string;
       image_url: string;
       name: string;
       email: string;
       whatsapp: string;
       city: string;
       uf: string;
    };
    items: {
        title: string;
    }[];
}


export interface SparklineOptions {
  show: boolean;
}


// Structure copied from angular
export interface SingleStatOptions extends SingleStatBaseOptions {
  sparkline: SparklineOptions;
  colorMode: ColorMode;
  displayMode: SingleStatDisplayMode;
}


export enum ColorMode {
  Thresholds,
  Series,
}


/*
*                      Copyright 2020 Salto Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export enum Keywords {
  TYPE_DEFINITION = 'type',
  VARIABLES_DEFINITION = 'vars',
  SETTINGS_DEFINITION = 'settings',
  LIST_DEFINITION = 'list',
  TYPE_INHERITANCE_SEPARATOR = 'is',
  ANNOTATIONS_DEFINITION = 'annotations',
  NAMESPACE_SEPARATOR = '.',

  // Primitive types
  TYPE_STRING = 'string',
  TYPE_NUMBER = 'number',
  TYPE_BOOL = 'boolean',
  TYPE_OBJECT = 'object',

  // Generics Types
  LIST_PREFIX = 'List<',
  GENERICS_SUFFIX = '>'
}


export interface RespCoreCacheMiss {
  time: number[];
  values: {
    mpki: {
      l1d_mpki: number[],
      l1i_mpki: number[],
      l2d_mpki: number[],
      l2i_mpki: number[],
    },
    percentage: {
      l1d_missrate: number[],
      l1i_missrate: number[],
      l2d_missrate: number[],
      l2i_missrate: number[],
    }
  };
}


(): JSX.Element => {
	return <BlogSearch />;
}


(name, value) => {
    this.setState({
      isInline: value === 'inline',
    });
  }


export default class Grid extends Component<any, any> {
  state = {
    isInline: false,
  };

  onChange = (name, value) => {
    this.setState({
      isInline: value === 'inline',
    });
  };

  render() {
    const { isInline } = this.state;
    return (
      <Form {...formDefaultProps}>
        <RadioGrid
          {...inputDefaultProps}
          isInline={isInline}
          options={defaultOptions.slice(0, 5)}
          // onBlur={this.onBlur}
          // onFocus={this.onFocus}
          label="With change, blur & focus handlers"
          questions={[
            { id: 1, name: 'Test question' },
            {
              id: 2,
              name:
                'Lei dice che è solo stanchezza, ma secondo me non può essere.. vediamo i prossimi giorni come sta',
            },
          ]}
        />
      </Form>
    );
  }
}


render() {
    const { isInline } = this.state;
    return (
      <Form {...formDefaultProps}>
        <RadioGrid
          {...inputDefaultProps}
          isInline={isInline}
          options={defaultOptions.slice(0, 5)}
          // onBlur={this.onBlur}
          // onFocus={this.onFocus}
          label="With change, blur & focus handlers"
          questions={[
            { id: 1, name: 'Test question' },
            {
              id: 2,
              name:
                'Lei dice che è solo stanchezza, ma secondo me non può essere.. vediamo i prossimi giorni come sta',
            },
          ]}
        />
      </Form>
    );
  }


/**
 * The description of the IoT hub.
 */
export function getIotHubResource(args: GetIotHubResourceArgs, opts?: pulumi.InvokeOptions): Promise<GetIotHubResourceResult> {
    if (!opts) {
        opts = {}
    }

    if (!opts.version) {
        opts.version = utilities.getVersion();
    }
    return pulumi.runtime.invoke("azure-native:devices/v20181201preview:getIotHubResource", {
        "resourceGroupName": args.resourceGroupName,
        "resourceName": args.resourceName,
    }, opts);
}


export interface GetIotHubResourceArgs {
    /**
     * The name of the resource group that contains the IoT hub.
     */
    resourceGroupName: string;
    /**
     * The name of the IoT hub.
     */
    resourceName: string;
}


/**
 * The description of the IoT hub.
 */
export interface GetIotHubResourceResult {
    /**
     * The Etag field is *not* required. If it is provided in the response body, it must also be provided as a header per the normal ETag convention.
     */
    readonly etag?: string;
    /**
     * The resource identifier.
     */
    readonly id: string;
    /**
     * The resource location.
     */
    readonly location: string;
    /**
     * The resource name.
     */
    readonly name: string;
    /**
     * IotHub properties
     */
    readonly properties: outputs.devices.v20181201preview.IotHubPropertiesResponse;
    /**
     * IotHub SKU info
     */
    readonly sku: outputs.devices.v20181201preview.IotHubSkuInfoResponse;
    /**
     * The resource tags.
     */
    readonly tags?: {[key: string]: string};
    /**
     * The resource type.
     */
    readonly type: string;
}


() => {
  let component: TextFieldEditorValidationComponent;
  let fixture: ComponentFixture<TextFieldEditorValidationComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        TextFieldEditorValidationComponent,
        TextFieldComponent
      ],
      imports: [
        FormsModule
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TextFieldEditorValidationComponent);
    component = fixture.componentInstance;
    component.component = new TextFieldComponent();
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
}


() => {
    TestBed.configureTestingModule({
      declarations: [
        TextFieldEditorValidationComponent,
        TextFieldComponent
      ],
      imports: [
        FormsModule
      ]
    })
    .compileComponents();
  }