JSFiddle - React, Tailwind, and code Playground

TypeScript

const pots = [
    ["A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8"],
    ["B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"],
    ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8"],
    ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8"],
];

const MATCH_COUNT = 144;
const MAX_ROUNDS = 8

function run() {
    const teamIds = _.flatten(pots)
    return teamIds.reduce((matches,thisTeamId) => {
        for (let round = 0; round < MAX_ROUNDS; round++) {
            const thisTeamMatches = matches.filter(match => match.includes(thisTeamId))
            if (thisTeamMatches.length >= MAX_ROUNDS) {
                break;
            }
            const pool = teamIds.filter(poolTeamId => {
                const encounteredBefore = thisTeamMatches.find(match => match.includes(poolTeamId))
                const potEncounterCount = thisTeamMatches.filter(match => {
                    const opponentId = match.find(m => m != thisTeamId)
                    return getTeamPot(opponentId, pots) === getTeamPot(poolTeamId, pots)
                })
                const poolTeamIdMatches = matches.filter(match => match.includes(poolTeamId))
                return poolTeamId != thisTeamId && !encounteredBefore && potEncounterCount.length < 2 && poolTeamIdMatches.length < MAX_ROUNDS
            })
            const opponent = _.sample(pool)
            matches.push([thisTeamId, opponent])
        }
        return matches
    }, [] as string[][])
}



function getTeamPot(teamId: string, pots: string[][]) {
    return pots.findIndex((pot) =>
        pot.find((potTeamId) => potTeamId === teamId),
    );
}

function getOpponent(yourTeamId: string, match: string[][]){
    return match.find(m => m != thisTeamId)                 
}

console.log(run())