Code structure review

Hello, I was wondering if anyone had the time to do a code review and take a glance at the structure, what could be improved, I dropped the code and stopped working on it as I just wasn’t satisfied, I felt like it was messy but I don’t know why? Maybe someone can give me a better insight

I’m not exactly the most experienced coder, this was my first attempt at an inventory system

-- INVENTORY UI SCRIPT (CLIENT), Creates and makes UI functional

--//Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
local SoundService = game:GetService("SoundService")
--//Variables
local sharedModules = ReplicatedStorage:WaitForChild("Shared")
local uiFolder = script.Parent

-- players
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")

-- tweens/sounds
local uiSound = SoundService:WaitForChild("UiSound")
local tweenClick = TweenInfo.new(0.15, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, 0, true)
local tweenHover = TweenInfo.new(0.15,Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, 0)

-- gui variables
local gui = playerGui:WaitForChild("MainUI")
local inventory = gui:WaitForChild("Inventory")
local main = inventory:WaitForChild("Main")
local inventories = main:WaitForChild("Inv")
local template = main:WaitForChild("TemplateButton"):WaitForChild("Template")
local categories = main:WaitForChild("Categories")
local sideBtns = main:WaitForChild("SideButtons")
local searchBox = categories:WaitForChild("Search"):WaitForChild("TextBox")
--//Requires
local Data = require(script.Parent.Parent:WaitForChild("DataHandler"))
local Types = require(sharedModules:WaitForChild("Types"))
local ItemData = require(sharedModules:WaitForChild("ItemData"))
local Networking = require(sharedModules:WaitForChild("Networking"))
local UIWeights = require(script:WaitForChild("UIWeights"))
local UIManger = require(uiFolder:WaitForChild("UIManager"))

-- module
local InventoryUI = {}
InventoryUI.EquipDebounce = false
InventoryUI.CurrentTab = "Weapons"
InventoryUI.CurrentCategory = "All"

-- Startup function
function InventoryUI.Init()
	
	-- Wait for inventory
	while not Data.Inventory do task.wait() return end
	
	-- Inventory updated
	Networking:Connect("InventoryChanged", InventoryUI.UpdateInventory)
	
	-- Create buttons
	InventoryUI.CreateButtons()
	
	-- When searching through UI
	searchBox:GetPropertyChangedSignal("Text"):Connect(InventoryUI.FilterSearch)
	
	-- When selecting a side button tab
	for _,tab in pairs(sideBtns:GetChildren()) do
		if tab:IsA("ImageButton") then
			tab.Activated:Connect(function()
				InventoryUI.SelectTab(tab)
			end)
		end
	end
	
	-- Category Selection
	for _,btn in pairs(categories:GetChildren()) do
		if btn:IsA("ImageButton") then
			btn.Activated:Connect(function()
				InventoryUI.SelectCategory(btn.Name)
			end)
		end
	end
end

-- Search through inventory
function InventoryUI.FilterSearch()
	
	-- Eventually switch the UI to filtering only the current category rather than everything :)
	
	-- Play typing SFX
	local sound = Instance.new("Sound")
	sound.Name = "Typing"
	sound.SoundId = "rbxassetid://9116156872"
	sound.Volume = 0.1
	sound.Parent = SoundService
	sound:Play()
	sound.Ended:Connect(function() sound:Destroy() end)

	local function match_strings(string1, string2)
		local matches = 0
		for i = 1, #string1 do
			if string.sub(string1, i, i) == string.sub(string2, i, i) then
				matches = matches + 1
			end
		end
		return matches == #string1
	end

	local function SortBySearch()
		local search_text = string.lower(searchBox.Text)

		for _, frame in pairs(inventories[InventoryUI.CurrentTab]:GetChildren()) do
				if frame:IsA("Frame") then
					local item_Name = string.lower(frame.Name)
					local isMatch = match_strings(search_text, item_Name)
					frame.Visible = (search_text == "" or isMatch)
				end
			end
		end
	SortBySearch()
end

-- Inventory tab switching
function InventoryUI.SelectTab(tab)
	local selectedTab = tab.Name
	if selectedTab == InventoryUI.CurrentTab then return end
	
	-- Inventory frame tabs
	local newInvTab = inventories:FindFirstChild(selectedTab)
	local oldInvTab = inventories:FindFirstChild(InventoryUI.CurrentTab)
	
	-- Side button Tabs
	local newSideBtn = sideBtns:FindFirstChild(selectedTab)
	local oldSideBtn = sideBtns:FindFirstChild(InventoryUI.CurrentTab)
	
	-- Setting search box empty
	searchBox.Text = ""
	
	-- Setting new tab
	InventoryUI.CurrentTab = selectedTab
	
	-- Setting visiblity
	newInvTab.Visible = true
	oldInvTab.Visible = false
	
	-- Setting images
	newSideBtn.Image = "rbxassetid://86775568773093"
	oldSideBtn.Image = "rbxassetid://119900627073534"
	
	InventoryUI.SelectCategory("All")
end

-- Inventory category selection
function InventoryUI.SelectCategory(newCategory)
	-- Debounce
	if newCategory == InventoryUI.CurrentCategory then return end
	
	local inv : Types.Inventory = Data.Inventory
	local oldCategory = InventoryUI.CurrentCategory
	
	-- Sort by all
	if newCategory == "All" then
		for _, cat in pairs(inventories:GetChildren()) do
			for _, frame in pairs(cat:GetChildren()) do
				if frame:IsA("Frame") then
					frame.Visible = true
				end
			end
		end
	end
	
	-- Sort by favorites
	if newCategory == "Favorites" then
		for name, data in inv.Owned[InventoryUI.CurrentTab] do
			if not data.Favorited then
				local frame = inventories[InventoryUI.CurrentTab][name]
				frame.Visible = false
			end
		end
	end
	
	-- Sort by classic tag
	if newCategory == "Classic" then
		
	end
	
	-- Sort by season
	if newCategory == "Season" then
		
	end
	
	-- Setting images and category
	categories[oldCategory].Image = "rbxassetid://98232073756262"
	categories[newCategory].Image = "rbxassetid://102068221649021"
	InventoryUI.CurrentCategory = newCategory
end

-- Update a specific frame
function InventoryUI.UpdateInventory(toUpdate, category, itemType)
	
	-- Inventory
	local inv : Types.Inventory = Data.Inventory
	
	-- Data information
	local itemInfo = inv.Owned[category][toUpdate]
	local staticData : Types.ItemData = ItemData[category][toUpdate]
	local itemF = inventories[category][toUpdate]
	local button = itemF[toUpdate]
	local favorite = button.Favorite
	
	-- Update equipped
	if inv.Equipped[itemType] == toUpdate then
		if itemType == "Knife" then
			itemF.LayoutOrder = 1
		elseif itemType == "Gun" then
			itemF.LayoutOrder = 2
		else
			itemF.LayoutOrder = 1 - UIWeights[staticData.Rarity].Weight
		end
		button.Image = UIWeights[staticData.Rarity].Equipped
	elseif typeof(inv.Equipped[itemType]) == "table" and table.find(inv.Equipped[itemType], toUpdate) then
		itemF.LayoutOrder = 1 - UIWeights[staticData.Rarity].Weight
		button.Image = UIWeights[staticData.Rarity].Equipped
	-- Update owned
	elseif inv.Owned[category][toUpdate] then
		itemF.LayoutOrder = UIWeights[staticData.Rarity].Weight
		button.Image = UIWeights[staticData.Rarity].Unequipped
	end
	
	-- Set favorited
	if itemInfo.Favorited then favorite.Image = "rbxassetid://76138845253841" else favorite.Image = "rbxassetid://91616516080222" end
end

-- Create the inventory frame buttons on launch
function InventoryUI.CreateButtons()
	-- Inventory
	local inv : Types.Inventory = Data.Inventory
	
	-- Loop through inventory
	for category, group in pairs(inv.Owned) do
		for name, data in pairs(group) do
			
			-- Static item data
			local staticData : Types.ItemData = ItemData[category][name]
			local itemType = staticData.ItemType
			local equipped = inv.Equipped[itemType]
			
			-- Creating button/frames
			local itemF = template:Clone()
			local button = itemF:WaitForChild("Template")
			local favorite = button:WaitForChild("Favorite")

			local image = button:WaitForChild("Image")
			local rarity = button:WaitForChild("RarityTxt")
			local btnname = button:WaitForChild("NameTxt")
			
			-- Button/Frame setup
			btnname.Text = staticData.Name
			rarity.TextColor3 = UIWeights[staticData.Rarity].Color
			button.Name = staticData.Name
			rarity.Text = staticData.Rarity
			image.Image = staticData.ImageId 
			itemF.Name  = staticData.Name
			itemF.Parent = inventories[category]
			itemF.Visible = true
			
			-- Favorite image
			if data.Favorited then favorite.Image = "rbxassetid://76138845253841" end
			
			-- Equip order / rarity bg
			if equipped == name or typeof(equipped) == "table" and table.find(equipped, name) then
				if itemType == "Knife" then
					itemF.LayoutOrder = 1
				elseif itemType == "Gun" then
					itemF.LayoutOrder = 2
				else
					itemF.LayoutOrder = 1 - UIWeights[staticData.Rarity].Weight
				end
				button.Image = UIWeights[staticData.Rarity].Equipped
			else
				button.Image = UIWeights[staticData.Rarity].Unequipped
				itemF.LayoutOrder = UIWeights[staticData.Rarity].Weight
			end
			
			-- Visual equip attempt
			button.MouseButton1Down:Connect(function()
				
				-- Set debounce, prevent unequipping knives/guns
				if InventoryUI.EquipDebounce then return end
				if inv.Equipped[itemType] == name and itemType == "Knife" or inv.Equipped[itemType] == name and itemType == "Gun" then
					return
				end
				
				-- Debounce start
				InventoryUI.EquipDebounce = true
				
				-- Create sound instance
				local sound = Instance.new("Sound")
				sound.Name = "Click"
				sound.SoundId = "rbxassetid://10066936758"
				sound.Parent = SoundService
				sound:Play()
				sound.Ended:Connect(function() sound:Destroy() end)
				
				-- Fire event to server
				Networking:FireServer("UpdateInventory", category, name)
				
				--Tween
				local tween = TweenService:Create(button, tweenClick, {Size = button.Size + UDim2.new(0.07,0,0.07,0)})
				tween:Play()
				tween.Completed:Wait()
				
				-- Debounce end
				InventoryUI.EquipDebounce = false
			end)
			
			-- favorite attempt
			favorite.MouseButton1Down:Connect(function()
				-- Sounds
				local sound = Instance.new("Sound")
				sound.Name = "Favorite"
				sound.Parent = SoundService
				sound.SoundId = "rbxassetid://10066942189"
				sound.Volume = 0.3
				sound:Play()
				sound.Ended:Connect(function() sound:Destroy() end)
				
				-- Fire to server
				Networking:FireServer("FavoriteAttempt", category, name)
			end)
			
			-- Mouse hover over buttons
			button.MouseEnter:Connect(function()
				local tween = TweenService:Create(button, tweenHover, {Size = UDim2.new(1.03,0,1.03,0)})
				tween:Play()
			end)
			
			-- Mouse leave hover buttons
			button.MouseLeave:Connect(function()
				local tween = TweenService:Create(button, tweenHover, {Size = UDim2.new(1,0,1,0)})
				tween:Play()
			end)
		end
	end
end

-- returning
return InventoryUI

-- DATA HANDLER (CLIENT), receives a client version of the inventory and makes changes based on it
--//Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--//Variables
local sharedModules = ReplicatedStorage:WaitForChild("Shared")
--//Requires
local Networking = require(sharedModules.Networking)
local ItemData = require(sharedModules.ItemData)
local Types = require(sharedModules.Types)

-- module
local DataHandler = {}
DataHandler.Inventory = nil

function DataHandler.Init()

	-- Get and set inventory data for local player
	local data = Networking:InvokeServer("GetInventoryData")
	DataHandler.Inventory = data
	
	-- When the inventory gets update request from server
	Networking:BindEvent("UpdateInventory", DataHandler.UpdateInventory)
	
end

function DataHandler.UpdateInventory(changedData)
	-- Update inventory here
	local inv : Types.Inventory = DataHandler.Inventory
	
	-- Preform correct updates
	if changedData.Action == "Equip" then
		
		-- Unequip item from local table
		if changedData.Unequip then
			
			local change = inv.Equipped[changedData.ItemType]
			
			if typeof(change) == "string" or change == nil then
				inv.Equipped[changedData.ItemType] = nil
			elseif typeof(change) == "table" then
				local index = table.find(change, changedData.Unequip)
				if index then
					table.remove(inv.Equipped[changedData.ItemType], index)
				end
			end
			Networking:Fire("InventoryChanged", changedData.Unequip, changedData.Category, changedData.ItemType)
		end
		
		-- Equip item to table
		if changedData.Equip then
			
			local change = inv.Equipped[changedData.ItemType]
			
			if typeof(change) == "string" or change == nil then
				inv.Equipped[changedData.ItemType] = changedData.Equip
			elseif typeof(change) == "table" then
				table.insert(inv.Equipped[changedData.ItemType], changedData.Equip)
			end
			Networking:Fire("InventoryChanged", changedData.Equip, changedData.Category, changedData.ItemType)
		end
	end
	
	if changedData.Action == "Favorite" then
		local item = inv.Owned[changedData.Category][changedData.ToFavorite]
		item.Favorited = not item.Favorited
		Networking:Fire("InventoryChanged", changedData.ToFavorite, changedData.Category, changedData.ItemType)
	end
end


-- returning
return DataHandler

![8mb.video-r4H-ePBfePGA|video](upload://k00PTViGsXwNny3r13kJPaHz6JJ.mp4)

I’d say just use more functions for it to stay more organized
Like for the unequip item and equip item just have them as seperate functions

function DataHandler.UpdateInventory(changedData)
	-- Update inventory here
	local inv : Types.Inventory = DataHandler.Inventory
	
	-- Preform correct updates
	if changedData.Action == "Equip" then
		
		-- Unequip item from local table
		if changedData.Unequip then
			
			UnEquip(item)
		end
		
		-- Equip item to table
		if changedData.Equip then
			
			Equip(item)
		end
	end
	
	if changedData.Action == "Favorite" then
		Favorite(item)
	end
end
4 Likes