import { ISpreadsheetService } from "../interfaces/ISpreadsheetService"; export class GASSpreadsheetService implements ISpreadsheetService { private getSheet(sheetName: string): GoogleAppsScript.Spreadsheet.Sheet { const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName); if (!sheet) { throw new Error(`Sheet '${sheetName}' not found`); } return sheet; } private getColumnIndex(sheet: GoogleAppsScript.Spreadsheet.Sheet, columnName: string): number { const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; return headers.indexOf(columnName); } getHeaders(sheetName: string): string[] { const sheet = this.getSheet(sheetName); return sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; } getRowData(sheetName: string, row: number): any[] { const sheet = this.getSheet(sheetName); const lastCol = sheet.getLastColumn(); // getRange(row, column, numRows, numColumns) return sheet.getRange(row, 1, 1, lastCol).getValues()[0]; } getRowNumberByColumnValue(sheetName: string, columnName: string, value: any): number | undefined { const sheet = this.getSheet(sheetName); const colIndex = this.getColumnIndex(sheet, columnName); if (colIndex === -1) return undefined; // Get all values in the column. Note: calling getValues() on a large sheet might be slow, // but this matches the previous implementation's performance characteristics more or less. // Ideally we would cache this or use a more efficient find. // offset(1, colIndex) to skip header, but actually getColumnValuesByName usually gets everything including header or handles it. // Original implementation: getColumnValuesByName gets range from row 2. const data = sheet.getRange(2, colIndex + 1, sheet.getLastRow() - 1, 1).getValues(); const flatData = data.map(r => r[0]); const index = flatData.indexOf(value); if (index === -1) return undefined; return index + 2; // +1 for 0-based index, +1 for header row } setCellValueByColumnName(sheetName: string, row: number, columnName: string, value: any): void { const sheet = this.getSheet(sheetName); const colIndex = this.getColumnIndex(sheet, columnName); if (colIndex !== -1) { sheet.getRange(row, colIndex + 1).setValue(value); } } getCellValueByColumnName(sheetName: string, row: number, columnName: string): any { const sheet = this.getSheet(sheetName); const colIndex = this.getColumnIndex(sheet, columnName); if (colIndex !== -1) { return sheet.getRange(row, colIndex + 1).getValue(); } return null; } getCellHyperlink(sheetName: string, row: number, columnName: string): string | null { const sheet = this.getSheet(sheetName); const colIndex = this.getColumnIndex(sheet, columnName); if (colIndex !== -1) { return sheet.getRange(row, colIndex + 1).getRichTextValue().getLinkUrl(); } return null; } setCellHyperlink(sheetName: string, row: number, columnName: string, displayText: string, url: string): void { const sheet = this.getSheet(sheetName); const colIndex = this.getColumnIndex(sheet, columnName); if (colIndex !== -1) { const richText = SpreadsheetApp.newRichTextValue() .setText(displayText) .setLinkUrl(url) .build(); sheet.getRange(row, colIndex + 1).setRichTextValue(richText); } } }