Nft contract with sub collections audit

Hey
I wanted to make a NFT smart contract that would let me make sub collections inside the main contract. This is mostly because I sense that it is quite expensive to put any contract on main, so I thought this way I could reuse the same contract for a bunch of projects.

I should mention that im prepending a collection index into the tokenId. For instance 1100002 would be collection 11 token 2.. hope that makes sense.

I'm new to smart contract development so I might be doing a lot of stuff wrong. Any feedback would be much appreciated.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";


contract KomputerToken is ERC721, ERC721URIStorage, Ownable
{
    using Strings for uint256;

    struct Collection {
        bytes ipfs;
        bytes uri;
        address collectionOwner;
        uint256 mintPriceInWei;
        uint256 numberOfTokens;
        uint256 currentTokenIndex;
        bool saleIsActive;
    }

    Collection[] public collections;
    string baseURI = "https://gateway.pinata.cloud/ipfs/";
    constructor() ERC721("Komputerwolf", "KW2000") {
        //collections.push(Collection("", "", 0x0000000000000000000000000000000000000000, 0, 0, 0, false));
    }

    function createCollection(bytes memory ipfs, bytes memory uri, uint256 numberOfTokens, address collectionOwner, uint256 mintPriceInWei)
    external onlyOwner {
        collections.push(Collection(ipfs, uri, collectionOwner, mintPriceInWei, numberOfTokens, 1, false));
    }

    function editCollection(uint256 collectionIndex, bytes memory ipfs, bytes memory uri, uint256 numberOfTokens, address collectionOwner, uint256 mintPriceInWei)
    external onlyOwner {
        collections[collectionIndex] = Collection(ipfs, uri, collectionOwner, mintPriceInWei, numberOfTokens, 1, false);
    }

    function setCollectionActive(uint256 collectionIndex, bool state) external onlyOwner {
        collections[collectionIndex].saleIsActive = state;
    }

    function mintNFT(address recipient, uint256 collectionIndex) public payable {
        require(collections[collectionIndex].saleIsActive, "Sale must be active to mint");
        require(collections[collectionIndex].currentTokenIndex <= collections[collectionIndex].numberOfTokens, "Max token count has been reached");
        require(collections[collectionIndex].mintPriceInWei <= msg.value, "Ether value sent is not correct");

        address payable collectionOwner = payable(collections[collectionIndex].collectionOwner);
        collectionOwner.transfer(msg.value);

        uint256 newTokenId = generateTokenId(collectionIndex);
        _safeMint(recipient, newTokenId);
        collections[collectionIndex].currentTokenIndex += 1;
    }

    function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        uint256 collectionIndex = getCollectionIndexFromTokenId(tokenId);
        bytes memory collectionHash = abi.encodePacked(collections[collectionIndex].ipfs);
        uint256 tokenIndex = getTokenIndexFromTokenId(tokenId);
        bytes memory fullUrl = abi.encodePacked(baseURI, collectionHash, "/", tokenIndex.toString());
        return string(fullUrl);
    }

    function generateTokenId(uint256 collectionIndex) internal view returns (uint256){
        // format:  collection index -- index in collection 
        //          #0                  00000
        // (100000*1001)+1
        uint256 index = (10000 * collectionIndex) + collections[collectionIndex].currentTokenIndex;
        return index;
    }


    function getCollectionIndexFromTokenId(uint256 tokenId) internal pure returns (uint256)
    {
        uint a = tokenId / 10000;
        // first digits after last 4
        return a;
    }

    function getTokenIndexFromTokenId(uint256 tokenId) internal pure returns (uint256)
    {
        uint a = tokenId % 10000;
        // last 4 digits
        return a;
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }
    
    function getCollectionSaleActive(uint256 collectionIndex) public view returns (bool) {
        return collections[collectionIndex].saleIsActive;
    }
    
    function getCurrentTokenIndex(uint256 collectionIndex) public view returns (uint256) {
        return collections[collectionIndex].currentTokenIndex;
    }

    function getMaxTokenCount(uint256 collectionIndex) public view returns (uint256) {
        return collections[collectionIndex].numberOfTokens;
    }
}