Unlocking Item System Bugged With Value Mismatch

hello! i am in the works of making a system where players can unlock items buy selling them. players can see which items they have unlocked and not unlocked by looking for said item in thier index Frame, However there seems to be a bug that i cannot debug.

When unlocking a Item the module will unlock the item, but when i check the index frame the item has not been unlocked.

– see video here

External Media

as you can see when a player sells thier tool and the module does infact unlock the tool however the client side t cant register the change leaving the item still locked on the client but unlocked on the module side.

– module script

local unlockedModule = {}

local replicatedStorage = game:GetService("ReplicatedStorage")
local datastoreService = game:GetService("DataStoreService")
local unlockedData = datastoreService:GetDataStore("test90")

unlockedModule.itemStats = {
	["apple"] = {
		locked = true,
		image = "rbxassetid://5673482401"
	},
	["gold bar"] = {
		locked = true,
		image = "rbxassetid://18336886764"
	},
	["painting"] = {
		locked = true,
		image = "rbxassetid://18367945834"
	},
	["stolen bank info"] = {
		locked = true,
		image = "rbxassetid://18336886764"
	},
	
	["flower pot"] = {
		locked = true,
		image = "rbxassetid://18336886764"
	},

}

function unlockedModule:saveData(player, itemStats)
	local success, errorMessage = pcall(function()
		unlockedData:SetAsync(player.UserId .. "-unlockedStats", itemStats)
	end)

	if not success then
		warn("Failed to save unlocked items " .. errorMessage)
	end
end

function unlockedModule:loadData(player)
	local success, unlockedStatsTable = pcall(function()
		return unlockedData:GetAsync(player.UserId .. "-unlockedStats")
	end)

	if success and unlockedStatsTable then
		for itemName, attributes in pairs(unlockedModule.itemStats) do
			if not unlockedStatsTable[itemName] then
				unlockedStatsTable[itemName] = attributes
			else
				for attrName, attrValue in pairs(attributes) do
					if unlockedStatsTable[itemName][attrName] == nil then
						unlockedStatsTable[itemName][attrName] = attrValue
					end
				end
			end
		end
		return unlockedStatsTable
	else
		if not success then
			warn("Failed to load item stats: " .. unlockedStatsTable)
		end
		return unlockedModule.itemStats
	end
end

function unlockedModule:getItemAttribute(player, name, attribute)
	local unlockedItemStats = self:loadData(player)

	if unlockedItemStats[name] then
		return unlockedItemStats[name][attribute] or unlockedModule.itemStats[name][attribute]
	else
		return unlockedModule.itemStats[name][attribute]
	end
end

function unlockedModule:updateItemAttribute(player, name, attribute, value)
	local unlockedItemStats = self:loadData(player)

	if not unlockedItemStats[name] then
		unlockedItemStats[name] = {}
	end

	unlockedItemStats[name][attribute] = value

	self:saveData(player, unlockedItemStats)
end

unlockedModule.lockedItems = {}
unlockedModule.unlockedItems = {}

function unlockedModule:getUnlockedItems(player)
	local itemStatsTable = self:loadData(player)
	self.unlockedItems = {}

	for name, attributes in pairs(itemStatsTable) do
		if attributes["locked"] == false then
			table.insert(self.unlockedItems, name)
		end
	end

	return self.unlockedItems
end

function unlockedModule:getLockedItems(player)
	local itemStatsTable = self:loadData(player)
	self.lockedItems = {}

	for name, attributes in pairs(itemStatsTable) do
		if attributes["locked"] == true then
			table.insert(self.lockedItems, name)
		end
	end

	return self.lockedItems
end


function unlockedModule:unlockItem(player, itemName)
	local itemStatsTable = self:loadData(player)
	local UPDATEUI = replicatedStorage:WaitForChild("mechanics"):FindFirstChild("Ui"):FindFirstChild("itemIndex"):FindFirstChild("updateUnlock")

	if itemStatsTable[itemName]["locked"] == false then 
		warn(itemName .. " is already unlocked.")
		return 
	end

	self:updateItemAttribute(player, itemName, "locked", false)
	
	itemStatsTable = self:loadData(player) 

	repeat 
		task.wait() 
	until itemStatsTable[itemName]["locked"] == false
	

	print("module locked: " .. tostring(itemStatsTable[itemName]["locked"]))

	warn(itemName .. " has been unlocked")

	UPDATEUI:FireClient(player, itemName)
end


return unlockedModule

– server script

local replicatedStorage = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local itemStats = require(replicatedStorage:WaitForChild("server"):FindFirstChild("ItemsWorth"))


players.PlayerAdded:Connect(function(player)
	local playerStats = player:FindFirstChild("playerStats")
	local otherStats = player:FindFirstChild("otherStats")

	local function waitForChild(parent, childName, timeout)
		local startTime = tick()
		while not parent:FindFirstChild(childName) do
			if timeout and (tick() - startTime) >= timeout then
				warn(childName .. " not found within timeout")
				return nil
			end
			task.wait(0.1)
		end
		return parent:FindFirstChild(childName)
	end

	playerStats = waitForChild(player, "playerStats", 10)
	otherStats = waitForChild(player, "otherStats", 10)

	if not playerStats or not otherStats then
		warn("playerStats or otherStats not found within the given timeout.")
		return
	end
	
	local playerGui = player:WaitForChild("PlayerGui")
	local scrollingFrame = playerGui:WaitForChild("uiStats"):WaitForChild("itemIndexFrame"):WaitForChild("indexFrame"):WaitForChild("ScrollingFrame")

	local unlockedItemsModule = require(replicatedStorage:WaitForChild("client"):WaitForChild("unlockedIndexModule"))
	
	local playerlevel = playerStats:WaitForChild("level")

	local unlockRemoteEvent = replicatedStorage:WaitForChild("mechanics"):FindFirstChild("Ui"):FindFirstChild("itemIndex"):FindFirstChild("unlockEvent")
	local function updateUi()
		
		local unlockedItemsTable = unlockedItemsModule:getUnlockedItems(player)
		local lockedItemsTable = unlockedItemsModule:getLockedItems(player)
		
		for _, child in pairs(scrollingFrame:GetChildren())  do
			if child:IsA("TextButton") then
				child:Destroy()
			end
		end
		
		print("cleared")
		
		for _, weaponName in ipairs(unlockedItemsTable) do
			local template = replicatedStorage:WaitForChild("mechanics"):FindFirstChild("Ui"):FindFirstChild("itemIndex"):WaitForChild("indexTemplate"):Clone()
			
			unlockedItemsModule:updateItemAttribute(player, weaponName, "locked", false)
			template.LayoutOrder = 0

			template.indexImage.Image = unlockedItemsModule:getItemAttribute(player, weaponName, "image")
			template.indextText.Text = weaponName

			template.lockedFrame:Destroy()

			template.Name = weaponName
			

			template.Parent = scrollingFrame
		end
		
		for _, weaponName in ipairs(lockedItemsTable) do
			local template = replicatedStorage:WaitForChild("mechanics"):FindFirstChild("Ui"):FindFirstChild("itemIndex"):WaitForChild("indexTemplate"):Clone()
			
			unlockedItemsModule:updateItemAttribute(player, weaponName, "locked", true)
			template.LayoutOrder = 1

			template.indexImage.Image = unlockedItemsModule:getItemAttribute(player, weaponName, "image")
			template.indextText.Text = weaponName

			template.lockedFrame.Visible = true

			template.Name = weaponName
			
			template.Parent = scrollingFrame
		end
	end
	
	updateUi()
	

end)


local event = replicatedStorage:WaitForChild("mechanics"):WaitForChild("other"):WaitForChild("getAtributes")

event.OnServerInvoke = function(player, module, name, value)
	
	if module == "itemM" then
		local itemStats = require(replicatedStorage:WaitForChild("server"):FindFirstChild("ItemsWorth"))
		local i = itemStats:getItemAttribute(player, name, value)
		return i 
	end
	
	if module == "unlockM" then
		local unlockedItemsModule = require(replicatedStorage:WaitForChild("client"):WaitForChild("unlockedIndexModule"))

		local u = unlockedItemsModule:getItemAttribute(player, name, value)
		return u
	end
end

– client script

local replicatedStorage = game:GetService("ReplicatedStorage")
local player = game:GetService("Players").LocalPlayer

local event = replicatedStorage:WaitForChild("mechanics"):WaitForChild("other"):WaitForChild("getAtributes")

local button = script.Parent
local indexShower = button.Parent.Parent.Parent.indexShower

local function buttonClicked()
	
	local lockedValue = event:InvokeServer("unlockM", button.Name, "locked")
	local level = event:InvokeServer("itemM", button.Name, "requiredLevel")
	
	print("client locked: " .. tostring(lockedValue))
	
	indexShower.itemImage.Image = button.indexImage.Image
	indexShower.itemName.Text = button.Name

	indexShower.itemLevel.Text = "Level: " ..level
	
	if lockedValue == false then
		indexShower.itemLocked.Text = "unlocked"
		indexShower.itemLocked.TextColor3 = Color3.fromRGB(255,255,255)
		return
	end
	
	if lockedValue == true then
		indexShower.itemLocked.Text = "locked"
		indexShower.itemLocked.TextColor3 =  Color3.fromRGB(255, 79, 79)
		return
	end
end

button.MouseButton1Click:Connect(buttonClicked)